mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-26 17:53:12 -05:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d24a518bac | ||
|
|
46b821d832 | ||
|
|
637bb30e13 | ||
|
|
b930ebfb20 | ||
|
|
5e2c40731c | ||
|
|
54ae810acc | ||
|
|
716c85cc3b | ||
|
|
3d1b76bcad | ||
|
|
4843a9a74a | ||
|
|
12aec943dc | ||
|
|
3b0d6050a2 | ||
|
|
3fd3661206 | ||
|
|
df8dd3fe4a | ||
|
|
a2c6b3f69b | ||
|
|
c01593e918 | ||
|
|
48484e5b1a | ||
|
|
173e8792a6 | ||
|
|
28047d9b58 | ||
|
|
82393b0cd1 | ||
|
|
eea9a6ae16 | ||
|
|
ca05c25b61 | ||
|
|
af912ebefb | ||
|
|
e6b46b21d9 | ||
|
|
a41f8b31f1 | ||
|
|
6271b33b1b | ||
|
|
09234e3bf0 | ||
|
|
9f467b702e | ||
|
|
6c156e0e14 | ||
|
|
10818ab0ba | ||
|
|
0778919134 | ||
|
|
fb8746e7b8 | ||
|
|
c82d08c0d9 | ||
|
|
be1dc69be6 | ||
|
|
8ea932ef7c | ||
|
|
70a6bc4769 | ||
|
|
c765401ac5 | ||
|
|
3b12a62fc6 | ||
|
|
c351cf7bd5 | ||
|
|
aea5eb3419 | ||
|
|
cb9008bb5c | ||
|
|
3534e445d8 | ||
|
|
c0ab7673ba | ||
|
|
abf73e08ec | ||
|
|
c4df8f0611 | ||
|
|
b28eefab77 | ||
|
|
6f3a139efd | ||
|
|
790f4a9b9a | ||
|
|
c7c87068bf | ||
|
|
f48dafd855 | ||
|
|
273f628acd | ||
|
|
a8653ea904 | ||
|
|
0093627adb | ||
|
|
2d73c703cb | ||
|
|
10315fe8f7 | ||
|
|
1f7d5a57af | ||
|
|
6d22df7b95 | ||
|
|
f5ddcdf193 | ||
|
|
36f6917308 | ||
|
|
53b5df92ae | ||
|
|
736352af5f | ||
|
|
41940b8003 | ||
|
|
641d24aa1e | ||
|
|
fb08a11ffe | ||
|
|
ba26378abc | ||
|
|
2a4b0d3d8b | ||
|
|
af4b3fa83d | ||
|
|
564012c53d | ||
|
|
2316a3fef9 | ||
|
|
a52fda72d6 | ||
|
|
cb05adeb48 | ||
|
|
f2eadd2908 | ||
|
|
4229061377 | ||
|
|
970ed1da7b | ||
|
|
dd9d9b85c1 | ||
|
|
6001d679e7 | ||
|
|
02cf53340b | ||
|
|
95042f1d40 | ||
|
|
34245687a5 | ||
|
|
7e5c750231 | ||
|
|
93c2df41c3 | ||
|
|
1e69577d12 | ||
|
|
cbafc28fa1 | ||
|
|
8622e19502 | ||
|
|
2fee1778ed | ||
|
|
2284ecb226 | ||
|
|
2716260473 | ||
|
|
0b89ab1a95 |
@@ -6,7 +6,7 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
__pycache__/
|
**/__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
*.so
|
*.so
|
||||||
@@ -25,9 +25,11 @@ venv
|
|||||||
|
|
||||||
*/node_modules
|
*/node_modules
|
||||||
*/dist
|
*/dist
|
||||||
|
/dist/
|
||||||
*/data/db
|
*/data/db
|
||||||
*/mealie/test
|
*/mealie/test
|
||||||
*/mealie/.temp
|
*/mealie/.temp
|
||||||
|
/mealie/frontend/
|
||||||
|
|
||||||
model.crfmodel
|
model.crfmodel
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/e2e.yml
vendored
14
.github/workflows/e2e.yml
vendored
@@ -3,8 +3,15 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
build-package:
|
||||||
|
name: "Build Python package"
|
||||||
|
uses: ./.github/workflows/partial-package.yml
|
||||||
|
with:
|
||||||
|
tag: e2e
|
||||||
|
|
||||||
test:
|
test:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
needs: build-package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -18,11 +25,18 @@ jobs:
|
|||||||
cache-dependency-path: ./tests/e2e/yarn.lock
|
cache-dependency-path: ./tests/e2e/yarn.lock
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Retrieve Python package
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: backend-dist
|
||||||
|
path: dist
|
||||||
- name: Build Image
|
- name: Build Image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
context: .
|
context: .
|
||||||
|
build-contexts: |
|
||||||
|
packages=dist
|
||||||
push: false
|
push: false
|
||||||
load: true
|
load: true
|
||||||
tags: mealie:e2e
|
tags: mealie:e2e
|
||||||
|
|||||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/partial-backend.yml
|
uses: ./.github/workflows/partial-backend.yml
|
||||||
|
|
||||||
frontend-tests:
|
frontend-tests:
|
||||||
name: "Frontend and End-to-End Tests"
|
name: "Frontend Tests"
|
||||||
uses: ./.github/workflows/partial-frontend.yml
|
uses: ./.github/workflows/partial-frontend.yml
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
|
|||||||
2
.github/workflows/partial-backend.yml
vendored
2
.github/workflows/partial-backend.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Backend Test/Lint
|
name: Backend Lint and Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|||||||
19
.github/workflows/partial-builder.yml
vendored
19
.github/workflows/partial-builder.yml
vendored
@@ -16,7 +16,14 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
build-package:
|
||||||
|
name: "Build Python package"
|
||||||
|
uses: ./.github/workflows/partial-package.yml
|
||||||
|
with:
|
||||||
|
tag: ${{ inputs.tag }}
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
|
needs: build-package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -35,18 +42,22 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Override __init__.py
|
|
||||||
run: |
|
|
||||||
echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py
|
|
||||||
|
|
||||||
- uses: depot/setup-action@v1
|
- uses: depot/setup-action@v1
|
||||||
|
|
||||||
|
- name: Retrieve Python package
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: backend-dist
|
||||||
|
path: dist
|
||||||
|
|
||||||
- name: Build and push Docker image, via Depot.dev
|
- name: Build and push Docker image, via Depot.dev
|
||||||
uses: depot/build-push-action@v1
|
uses: depot/build-push-action@v1
|
||||||
with:
|
with:
|
||||||
project: srzjb6mhzm
|
project: srzjb6mhzm
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
context: .
|
context: .
|
||||||
|
build-contexts: |
|
||||||
|
packages=dist
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
|
|||||||
36
.github/workflows/partial-frontend.yml
vendored
36
.github/workflows/partial-frontend.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Frontend Build/Lin
|
name: Frontend Lint and Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
@@ -41,37 +41,3 @@ jobs:
|
|||||||
- name: Run tests 🧪
|
- name: Run tests 🧪
|
||||||
run: yarn test:ci
|
run: yarn test:ci
|
||||||
working-directory: "frontend"
|
working-directory: "frontend"
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout 🛎
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup node env 🏗
|
|
||||||
uses: actions/setup-node@v4.0.0
|
|
||||||
with:
|
|
||||||
node-version: 16
|
|
||||||
check-latest: true
|
|
||||||
|
|
||||||
- name: Get yarn cache directory path 🛠
|
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Cache node_modules 📦
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
|
|
||||||
- name: Install dependencies 👨🏻💻
|
|
||||||
run: yarn
|
|
||||||
working-directory: "frontend"
|
|
||||||
|
|
||||||
- name: Run Build 🚚
|
|
||||||
run: yarn build
|
|
||||||
working-directory: "frontend"
|
|
||||||
|
|||||||
102
.github/workflows/partial-package.yml
vendored
Normal file
102
.github/workflows/partial-package.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
name: Package build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-frontend:
|
||||||
|
name: Build frontend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout 🛎
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup node env 🏗
|
||||||
|
uses: actions/setup-node@v4.0.0
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path 🛠
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache node_modules 📦
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
- name: Install dependencies 👨🏻💻
|
||||||
|
run: yarn
|
||||||
|
working-directory: "frontend"
|
||||||
|
|
||||||
|
- name: Run Build 🚚
|
||||||
|
run: yarn generate
|
||||||
|
working-directory: "frontend"
|
||||||
|
|
||||||
|
- name: Archive built frontend
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: frontend-dist
|
||||||
|
path: frontend/dist
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
build-package:
|
||||||
|
name: Build Python package
|
||||||
|
needs: build-frontend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install Task
|
||||||
|
uses: arduino/setup-task@v2
|
||||||
|
with:
|
||||||
|
version: 3.x
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
virtualenvs-create: true
|
||||||
|
virtualenvs-in-project: true
|
||||||
|
plugins: |
|
||||||
|
poetry-plugin-export
|
||||||
|
|
||||||
|
- name: Retrieve built frontend
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: frontend-dist
|
||||||
|
path: mealie/frontend
|
||||||
|
|
||||||
|
- name: Override __init__.py
|
||||||
|
run: |
|
||||||
|
echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py
|
||||||
|
|
||||||
|
- name: Build package and requirements.txt
|
||||||
|
env:
|
||||||
|
SKIP_PACKAGE_DEPS: true
|
||||||
|
run: |
|
||||||
|
task py:package
|
||||||
|
|
||||||
|
- name: Archive built package
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: backend-dist
|
||||||
|
path: dist
|
||||||
|
retention-days: 5
|
||||||
2
.github/workflows/pull-requests.yml
vendored
2
.github/workflows/pull-requests.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/partial-backend.yml
|
uses: ./.github/workflows/partial-backend.yml
|
||||||
|
|
||||||
frontend-tests:
|
frontend-tests:
|
||||||
name: "Frontend and End-to-End Tests"
|
name: "Frontend Tests"
|
||||||
uses: ./.github/workflows/partial-frontend.yml
|
uses: ./.github/workflows/partial-frontend.yml
|
||||||
|
|
||||||
container-scanning:
|
container-scanning:
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/partial-backend.yml
|
uses: ./.github/workflows/partial-backend.yml
|
||||||
|
|
||||||
frontend-tests:
|
frontend-tests:
|
||||||
name: "Frontend and End-to-End Tests"
|
name: "Frontend Tests"
|
||||||
uses: ./.github/workflows/partial-frontend.yml
|
uses: ./.github/workflows/partial-frontend.yml
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -52,7 +52,7 @@ pnpm-debug.log*
|
|||||||
env/
|
env/
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
|
/dist/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
@@ -66,6 +66,9 @@ wheels/
|
|||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
|
|
||||||
|
# frontend copied into Python module for packaging purposes
|
||||||
|
/mealie/frontend/
|
||||||
|
|
||||||
# PyInstaller
|
# PyInstaller
|
||||||
# Usually these files are written by a python script from a template
|
# Usually these files are written by a python script from a template
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ repos:
|
|||||||
exclude: ^tests/data/
|
exclude: ^tests/data/
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.9.2
|
rev: v0.9.7
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -60,5 +60,9 @@
|
|||||||
},
|
},
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
"editor.formatOnSave": false
|
"editor.formatOnSave": false
|
||||||
|
},
|
||||||
|
"[python]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
Taskfile.yml
86
Taskfile.yml
@@ -41,14 +41,25 @@ tasks:
|
|||||||
setup:ui:
|
setup:ui:
|
||||||
desc: setup frontend dependencies
|
desc: setup frontend dependencies
|
||||||
dir: frontend
|
dir: frontend
|
||||||
|
run: once
|
||||||
cmds:
|
cmds:
|
||||||
- yarn install
|
- yarn install
|
||||||
|
sources:
|
||||||
|
- package.json
|
||||||
|
- yarn.lock
|
||||||
|
generates:
|
||||||
|
- node_modules/**
|
||||||
|
|
||||||
setup:py:
|
setup:py:
|
||||||
desc: setup python dependencies
|
desc: setup python dependencies
|
||||||
|
run: once
|
||||||
cmds:
|
cmds:
|
||||||
- poetry install --with main,dev,postgres
|
- poetry install --with main,dev,postgres
|
||||||
- poetry run pre-commit install
|
- poetry run pre-commit install
|
||||||
|
sources:
|
||||||
|
- poetry.lock
|
||||||
|
- pyproject.toml
|
||||||
|
- .pre-commit-config.yaml
|
||||||
|
|
||||||
setup:model:
|
setup:model:
|
||||||
desc: setup nlp model
|
desc: setup nlp model
|
||||||
@@ -131,6 +142,63 @@ tasks:
|
|||||||
- poetry run coverage html
|
- poetry run coverage html
|
||||||
- open htmlcov/index.html
|
- open htmlcov/index.html
|
||||||
|
|
||||||
|
py:package:copy-frontend:
|
||||||
|
desc: copy the frontend files into the Python package
|
||||||
|
internal: true
|
||||||
|
deps:
|
||||||
|
- ui:generate
|
||||||
|
cmds:
|
||||||
|
- rm -rf mealie/frontend
|
||||||
|
- cp -a frontend/dist mealie/frontend
|
||||||
|
sources:
|
||||||
|
- frontend/dist/**
|
||||||
|
generates:
|
||||||
|
- mealie/frontend/**
|
||||||
|
|
||||||
|
py:package:generate-requirements:
|
||||||
|
desc: Generate requirements file to pin all packages, effectively a "pip freeze" before installation begins
|
||||||
|
internal: true
|
||||||
|
cmds:
|
||||||
|
- poetry export -n --only=main --extras=pgsql --output=dist/requirements.txt
|
||||||
|
# Include mealie in the requirements, hashing the package that was just built to ensure it's the one installed
|
||||||
|
- echo "mealie[pgsql]=={{.MEALIE_VERSION}} \\" >> dist/requirements.txt
|
||||||
|
- poetry run pip hash dist/mealie-{{.MEALIE_VERSION}}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt
|
||||||
|
- echo " \\" >> dist/requirements.txt
|
||||||
|
- poetry run pip hash dist/mealie-{{.MEALIE_VERSION}}.tar.gz | tail -n1 >> dist/requirements.txt
|
||||||
|
vars:
|
||||||
|
MEALIE_VERSION:
|
||||||
|
sh: poetry version --short
|
||||||
|
sources:
|
||||||
|
- poetry.lock
|
||||||
|
- pyproject.toml
|
||||||
|
- dist/mealie-*.whl
|
||||||
|
- dist/mealie-*.tar.gz
|
||||||
|
generates:
|
||||||
|
- dist/requirements.txt
|
||||||
|
|
||||||
|
py:package:deps-parallel:
|
||||||
|
desc: Run py:package dependencies in parallel
|
||||||
|
internal: true
|
||||||
|
deps:
|
||||||
|
- setup:py
|
||||||
|
- py:package:copy-frontend
|
||||||
|
|
||||||
|
py:package:deps:
|
||||||
|
desc: Dependencies of py:package, skippable by setting SKIP_PACKAGE_DEPS=true
|
||||||
|
internal: true
|
||||||
|
cmds:
|
||||||
|
- task: py:package:deps-parallel
|
||||||
|
status:
|
||||||
|
- '{{ .SKIP_PACKAGE_DEPS | default "false"}}'
|
||||||
|
|
||||||
|
py:package:
|
||||||
|
desc: builds Python packages (sdist and wheel) in top-level dist directory
|
||||||
|
deps:
|
||||||
|
- py:package:deps
|
||||||
|
cmds:
|
||||||
|
- poetry build -n --output=dist
|
||||||
|
- task: py:package:generate-requirements
|
||||||
|
|
||||||
py:
|
py:
|
||||||
desc: runs the backend server
|
desc: runs the backend server
|
||||||
cmds:
|
cmds:
|
||||||
@@ -160,6 +228,14 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- yarn build
|
- yarn build
|
||||||
|
|
||||||
|
ui:generate:
|
||||||
|
desc: generates a static version of the frontend in frontend/dist
|
||||||
|
dir: frontend
|
||||||
|
deps:
|
||||||
|
- setup:ui
|
||||||
|
cmds:
|
||||||
|
- yarn generate
|
||||||
|
|
||||||
ui:lint:
|
ui:lint:
|
||||||
desc: runs the frontend linter
|
desc: runs the frontend linter
|
||||||
dir: frontend
|
dir: frontend
|
||||||
@@ -184,6 +260,16 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- yarn run dev
|
- yarn run dev
|
||||||
|
|
||||||
|
docker:build-from-package:
|
||||||
|
desc: Builds the Docker image from the existing Python package in dist/
|
||||||
|
deps:
|
||||||
|
- py:package
|
||||||
|
cmds:
|
||||||
|
- docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT={{.GIT_COMMIT}} --build-context packages=dist .
|
||||||
|
vars:
|
||||||
|
GIT_COMMIT:
|
||||||
|
sh: git rev-parse HEAD
|
||||||
|
|
||||||
docker:prod:
|
docker:prod:
|
||||||
desc: builds and runs the production docker image locally
|
desc: builds and runs the production docker image locally
|
||||||
dir: docker
|
dir: docker
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
FROM node:16 as builder
|
###############################################
|
||||||
|
# Frontend Build
|
||||||
|
###############################################
|
||||||
|
FROM node:16 AS frontend-builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /frontend
|
||||||
|
|
||||||
COPY ./frontend .
|
COPY frontend .
|
||||||
|
|
||||||
RUN yarn install \
|
RUN yarn install \
|
||||||
--prefer-offline \
|
--prefer-offline \
|
||||||
@@ -26,14 +29,10 @@ ENV PYTHONUNBUFFERED=1 \
|
|||||||
PIP_NO_CACHE_DIR=off \
|
PIP_NO_CACHE_DIR=off \
|
||||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||||
PIP_DEFAULT_TIMEOUT=100 \
|
PIP_DEFAULT_TIMEOUT=100 \
|
||||||
POETRY_HOME="/opt/poetry" \
|
VENV_PATH="/opt/mealie"
|
||||||
POETRY_VIRTUALENVS_IN_PROJECT=true \
|
|
||||||
POETRY_NO_INTERACTION=1 \
|
|
||||||
PYSETUP_PATH="/opt/pysetup" \
|
|
||||||
VENV_PATH="/opt/pysetup/.venv"
|
|
||||||
|
|
||||||
# prepend poetry and venv to path
|
# prepend venv to path
|
||||||
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"
|
ENV PATH="$VENV_PATH/bin:$PATH"
|
||||||
|
|
||||||
# create user account
|
# create user account
|
||||||
RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \
|
RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \
|
||||||
@@ -41,31 +40,81 @@ RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \
|
|||||||
&& mkdir $MEALIE_HOME
|
&& mkdir $MEALIE_HOME
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Builder Image
|
# Backend Package Build
|
||||||
###############################################
|
###############################################
|
||||||
FROM python-base as builder-base
|
FROM python-base AS backend-builder
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install --no-install-recommends -y \
|
&& apt-get install --no-install-recommends -y \
|
||||||
curl \
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV POETRY_HOME="/opt/poetry" \
|
||||||
|
POETRY_NO_INTERACTION=1
|
||||||
|
|
||||||
|
# prepend poetry to path
|
||||||
|
ENV PATH="$POETRY_HOME/bin:$PATH"
|
||||||
|
|
||||||
|
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
|
||||||
|
ENV POETRY_VERSION=2.0.1
|
||||||
|
RUN curl -sSL https://install.python-poetry.org | python3 -
|
||||||
|
|
||||||
|
# install poetry plugins needed to build the package
|
||||||
|
RUN poetry self add "poetry-plugin-export>=1.9"
|
||||||
|
|
||||||
|
WORKDIR /mealie
|
||||||
|
|
||||||
|
# copy project files here to ensure they will be cached.
|
||||||
|
COPY poetry.lock pyproject.toml ./
|
||||||
|
COPY mealie ./mealie
|
||||||
|
|
||||||
|
# Copy frontend to package it into the wheel
|
||||||
|
COPY --from=frontend-builder /frontend/dist ./mealie/frontend
|
||||||
|
|
||||||
|
# Build the source and binary package
|
||||||
|
RUN poetry build --output=dist
|
||||||
|
|
||||||
|
# Create the requirements file, which is used to install the built package and
|
||||||
|
# its pinned dependencies later. mealie is included to ensure the built one is
|
||||||
|
# what's installed.
|
||||||
|
RUN export MEALIE_VERSION=$(poetry version --short) \
|
||||||
|
&& poetry export --only=main --extras=pgsql --output=dist/requirements.txt \
|
||||||
|
&& echo "mealie[pgsql]==$MEALIE_VERSION \\" >> dist/requirements.txt \
|
||||||
|
&& poetry run pip hash dist/mealie-$MEALIE_VERSION-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt \
|
||||||
|
&& echo " \\" >> dist/requirements.txt \
|
||||||
|
&& poetry run pip hash dist/mealie-$MEALIE_VERSION.tar.gz | tail -n1 >> dist/requirements.txt
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Package Container
|
||||||
|
# Only role is to hold the packages, or be overriden by a --build-context flag.
|
||||||
|
###############################################
|
||||||
|
FROM scratch AS packages
|
||||||
|
COPY --from=backend-builder /mealie/dist /
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Python Virtual Environment Build
|
||||||
|
###############################################
|
||||||
|
# Install packages required to build the venv, in parallel to building the wheel
|
||||||
|
FROM python-base AS venv-builder-base
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install --no-install-recommends -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
libpq-dev \
|
libpq-dev \
|
||||||
libwebp-dev \
|
libwebp-dev \
|
||||||
# LDAP Dependencies
|
# LDAP Dependencies
|
||||||
libsasl2-dev libldap2-dev libssl-dev \
|
libsasl2-dev libldap2-dev libssl-dev \
|
||||||
gnupg gnupg2 gnupg1 \
|
gnupg gnupg2 gnupg1 \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
&& pip install -U --no-cache-dir pip
|
RUN python3 -m venv --upgrade-deps $VENV_PATH
|
||||||
|
|
||||||
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
|
# Install the wheel and all dependencies into the venv
|
||||||
ENV POETRY_VERSION=1.3.1
|
FROM venv-builder-base AS venv-builder
|
||||||
RUN curl -sSL https://install.python-poetry.org | python3 -
|
|
||||||
|
|
||||||
# copy project requirement files here to ensure they will be cached.
|
# Copy built package (wheel) and its dependency requirements
|
||||||
WORKDIR $PYSETUP_PATH
|
COPY --from=packages * /dist/
|
||||||
COPY ./poetry.lock ./pyproject.toml ./
|
|
||||||
|
|
||||||
# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
|
# Install the wheel with exact versions of dependencies into the venv
|
||||||
RUN poetry install -E pgsql --only main
|
RUN . $VENV_PATH/bin/activate \
|
||||||
|
&& pip install --require-hashes -r /dist/requirements.txt --find-links /dist
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# CRFPP Image
|
# CRFPP Image
|
||||||
@@ -96,39 +145,25 @@ RUN apt-get update \
|
|||||||
# create directory used for Docker Secrets
|
# create directory used for Docker Secrets
|
||||||
RUN mkdir -p /run/secrets
|
RUN mkdir -p /run/secrets
|
||||||
|
|
||||||
# copying poetry and venv into image
|
# copy CRF++ and add it to the library path
|
||||||
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
|
|
||||||
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
|
|
||||||
|
|
||||||
ENV LD_LIBRARY_PATH=/usr/local/lib
|
ENV LD_LIBRARY_PATH=/usr/local/lib
|
||||||
COPY --from=crfpp /usr/local/lib/ /usr/local/lib
|
COPY --from=crfpp /usr/local/lib/ /usr/local/lib
|
||||||
COPY --from=crfpp /usr/local/bin/crf_learn /usr/local/bin/crf_learn
|
COPY --from=crfpp /usr/local/bin/crf_learn /usr/local/bin/crf_learn
|
||||||
COPY --from=crfpp /usr/local/bin/crf_test /usr/local/bin/crf_test
|
COPY --from=crfpp /usr/local/bin/crf_test /usr/local/bin/crf_test
|
||||||
|
|
||||||
# copy backend
|
# Copy venv into image. It contains a fully-installed mealie backend and frontend.
|
||||||
COPY ./mealie $MEALIE_HOME/mealie
|
COPY --from=venv-builder $VENV_PATH $VENV_PATH
|
||||||
COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/
|
|
||||||
|
|
||||||
# venv already has runtime deps installed we get a quicker install
|
|
||||||
WORKDIR $MEALIE_HOME
|
|
||||||
RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --only main
|
|
||||||
WORKDIR /
|
|
||||||
|
|
||||||
# Grab CRF++ Model Release
|
# Grab CRF++ Model Release
|
||||||
RUN python $MEALIE_HOME/mealie/scripts/install_model.py
|
RUN python -m mealie.scripts.install_model
|
||||||
|
|
||||||
VOLUME [ "$MEALIE_HOME/data/" ]
|
VOLUME [ "$MEALIE_HOME/data/" ]
|
||||||
ENV APP_PORT=9000
|
ENV APP_PORT=9000
|
||||||
|
|
||||||
EXPOSE ${APP_PORT}
|
EXPOSE ${APP_PORT}
|
||||||
|
|
||||||
HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1
|
HEALTHCHECK CMD python -m mealie.scripts.healthcheck || exit 1
|
||||||
|
|
||||||
# ----------------------------------
|
|
||||||
# Copy Frontend
|
|
||||||
|
|
||||||
ENV STATIC_FILES=/spa/static
|
|
||||||
COPY --from=builder /app/dist ${STATIC_FILES}
|
|
||||||
|
|
||||||
ENV HOST 0.0.0.0
|
ENV HOST 0.0.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -32,13 +32,51 @@ init() {
|
|||||||
cd /app
|
cd /app
|
||||||
|
|
||||||
# Activate our virtual environment here
|
# Activate our virtual environment here
|
||||||
. /opt/pysetup/.venv/bin/activate
|
. /opt/mealie/bin/activate
|
||||||
|
}
|
||||||
|
|
||||||
|
load_secrets() {
|
||||||
|
# Each of these environment variables will support a `_FILE` suffix that allows
|
||||||
|
# for setting the environment variable through the Docker Compose secret
|
||||||
|
# pattern.
|
||||||
|
local -a secret_supported_vars=(
|
||||||
|
"POSTGRES_USER"
|
||||||
|
"POSTGRES_PASSWORD"
|
||||||
|
"POSTGRES_SERVER"
|
||||||
|
"POSTGRES_PORT"
|
||||||
|
"POSTGRES_DB"
|
||||||
|
"POSTGRES_URL_OVERRIDE"
|
||||||
|
|
||||||
|
"SMTP_HOST"
|
||||||
|
"SMTP_PORT"
|
||||||
|
"SMTP_USER"
|
||||||
|
"SMTP_PASSWORD"
|
||||||
|
|
||||||
|
"LDAP_SERVER_URL"
|
||||||
|
"LDAP_QUERY_PASSWORD"
|
||||||
|
|
||||||
|
"OIDC_CONFIGURATION_URL"
|
||||||
|
"OIDC_CLIENT_ID"
|
||||||
|
"OIDC_CLIENT_SECRET"
|
||||||
|
|
||||||
|
"OPENAI_BASE_URL"
|
||||||
|
"OPENAI_API_KEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
# If any secrets are set, prefer them over base environment variables.
|
||||||
|
for var in "${secret_supported_vars[@]}"; do
|
||||||
|
file_var="${var}_FILE"
|
||||||
|
if [ -n "${!file_var}" ]; then
|
||||||
|
export "$var=$(<"${!file_var}")"
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
change_user
|
change_user
|
||||||
init
|
init
|
||||||
|
load_secrets
|
||||||
|
|
||||||
# Start API
|
# Start API
|
||||||
HOST_IP=`/sbin/ip route|awk '/default/ { print $3 }'`
|
HOST_IP=`/sbin/ip route|awk '/default/ { print $3 }'`
|
||||||
|
|
||||||
exec python /app/mealie/main.py
|
exec mealie
|
||||||
|
|||||||
40
docs/docs/contributors/developers-guide/building-packages.md
Normal file
40
docs/docs/contributors/developers-guide/building-packages.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Building Packages
|
||||||
|
|
||||||
|
Released packages are [built and published via GitHub actions](maintainers.md#drafting-releases).
|
||||||
|
|
||||||
|
## Python packages
|
||||||
|
|
||||||
|
To build Python packages locally for testing, use [`task`](starting-dev-server.md#without-dev-containers). After installing `task`, run `task py:package` to perform all the steps needed to build the package and a requirements file. To do it manually, run:
|
||||||
|
```sh
|
||||||
|
pushd frontend
|
||||||
|
yarnpkg install
|
||||||
|
yarnpkg generate
|
||||||
|
popd
|
||||||
|
rm -r mealie/frontend
|
||||||
|
cp -a frontend/dist mealie/frontend
|
||||||
|
poetry build
|
||||||
|
poetry export -n --only=main --extras=pgsql --output=dist/requirements.txt
|
||||||
|
MEALIE_VERSION=$(poetry version --short)
|
||||||
|
echo "mealie[pgsql]==${MEALIE_VERSION} \\" >> dist/requirements.txt
|
||||||
|
poetry run pip hash dist/mealie-${MEALIE_VERSION}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt
|
||||||
|
echo " \\" >> dist/requirements.txt
|
||||||
|
poetry run pip hash dist/mealie-${MEALIE_VERSION}.tar.gz | tail -n1 >> dist/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
The Python package can be installed with all of its dependencies pinned to the versions tested by the developers with:
|
||||||
|
```sh
|
||||||
|
pip3 install -r dist/requirements.txt --find-links dist
|
||||||
|
```
|
||||||
|
|
||||||
|
To install with the latest but still compatible dependency versions, instead run `pip3 install dist/mealie-$VERSION-py3-none-any.whl` (where `$VERSION` is the version of mealie to install).
|
||||||
|
|
||||||
|
## Docker image
|
||||||
|
One way to build the Docker image is to run the following command in the project root directory:
|
||||||
|
```sh
|
||||||
|
docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT=$(git rev-parse HEAD) .
|
||||||
|
```
|
||||||
|
|
||||||
|
The Docker image can be built from the pre-built Python packages with the task command `task docker:build-from-package`. This is equivalent to:
|
||||||
|
```sh
|
||||||
|
docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT=$(git rev-parse HEAD) --build-context packages=dist .
|
||||||
|
```
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
Mealie supports 3rd party authentication via [OpenID Connect (OIDC)](https://openid.net/connect/), an identity layer built on top of OAuth2. OIDC is supported by many Identity Providers (IdP), including:
|
Mealie supports 3rd party authentication via [OpenID Connect (OIDC)](https://openid.net/connect/), an identity layer built on top of OAuth2. OIDC is supported by many Identity Providers (IdP), including:
|
||||||
|
|
||||||
- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
|
- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
|
||||||
- [Authelia](https://www.authelia.com/configuration/identity-providers/open-id-connect/)
|
- [Authelia](https://www.authelia.com/integration/openid-connect/mealie/)
|
||||||
- [Keycloak](https://www.keycloak.org/docs/latest/securing_apps/#_oidc)
|
- [Keycloak](https://www.keycloak.org/docs/latest/securing_apps/#_oidc)
|
||||||
- [Okta](https://www.okta.com/openid-connect/)
|
- [Okta](https://www.okta.com/openid-connect/)
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ Mealie has a robust and flexible recipe organization system with a few different
|
|||||||
|
|
||||||
#### Categories
|
#### Categories
|
||||||
|
|
||||||
Categories are the overarching organizer for recipes. You can assign as many categories as you'd like to a recipe, but we recommend that you try to limit the categories you assign to a recipe to one or two. This helps keep categories as focused as possible while still allowing you to find recipes that are related to each other. For example, you might assign a recipe to the category **Breakfast**, **Lunch**, **Dinner**, or **Side**.
|
Categories are the overarching organizer for recipes. You can assign as many categories as you'd like to a recipe, but we recommend that you try to limit the categories you assign to a recipe to one or two. This helps keep categories as focused as possible while still allowing you to find recipes that are related to each other. For example, you might assign a recipe to the category **Breakfast**, **Lunch**, **Dinner**, **Side**, or **Drinks**.
|
||||||
|
|
||||||
[Categories Demo](https://demo.mealie.io/g/home/recipes/categories){ .md-button .md-button--primary }
|
[Categories Demo](https://demo.mealie.io/g/home/recipes/categories){ .md-button .md-button--primary }
|
||||||
|
|
||||||
@@ -84,6 +84,29 @@ The meal planner has the concept of plan rules. These offer a flexible way to us
|
|||||||
|
|
||||||
The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list or link a recipe and all of it's ingredients to track meals during the week.
|
The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list or link a recipe and all of it's ingredients to track meals during the week.
|
||||||
|
|
||||||
|
Managing shopping lists can be done from the Sidebar > Shopping Lists.
|
||||||
|
|
||||||
|
Here you will be able to:
|
||||||
|
- See items already on the Shopping List
|
||||||
|
- See linked recipes with ingredients
|
||||||
|
- Toggling via the 'Pot' icon will show you the linked recipe, allowing you to click to access it.
|
||||||
|
- Check off an item
|
||||||
|
- Add / Change / Remove / Sort Items via the grid icon
|
||||||
|
- Be sure if you are modifying an ingredient to click the 'Save' icon.
|
||||||
|
- Add / Change / Remove / Sort Labels
|
||||||
|
- 'No Label' will always be on the top, others can be Reordered via the 'Reorder Labels' button
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If you accidentally checked off an item, you can uncheck it by expanding 'items checked' and unchecking it. This will add it back to the Shopping List.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
You can use Labels to categorize your ingredients. You may want to Label by Food Type (Frozen, Fresh, etc), by Store, Tool, Recipe, or more. Play around with this to see what works best for you.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
You can toggle 'Food' on items so that if you add multiple of the same food / ingredient, Mealie will automatically combine them together. Do this by editing an item in the Shopping List and clicking the 'Apple' icon. If you then have recipes that contain "1 | cup | cheese" and "2 | cup | cheese" this would be combined to show "3 cups of cheese."
|
||||||
|
|
||||||
|
[See FAQ for more information](../getting-started/faq.md)
|
||||||
|
|
||||||
|
|
||||||
[Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
|
[Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
|
||||||
|
|
||||||
@@ -94,9 +117,9 @@ Mealie is designed to integrate with many different external services. There are
|
|||||||
### Notifiers
|
### Notifiers
|
||||||
|
|
||||||
Notifiers are event-driven notifications sent when specific actions are performed within Mealie. Some actions include:
|
Notifiers are event-driven notifications sent when specific actions are performed within Mealie. Some actions include:
|
||||||
- creating a recipe
|
- Creating / Updating a recipe
|
||||||
- adding items to a shopping list
|
- Adding items to a shopping list
|
||||||
- creating a new mealplan
|
- Creating a new mealplan
|
||||||
|
|
||||||
Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), which integrates with a large number of notification services. In addition, certain custom notifiers send basic event data to the consumer (e.g. the `id` of the resource). These include:
|
Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), which integrates with a large number of notification services. In addition, certain custom notifiers send basic event data to the consumer (e.g. the `id` of the resource). These include:
|
||||||
|
|
||||||
@@ -139,6 +162,9 @@ Below is a list of all valid merge fields:
|
|||||||
- ${id}
|
- ${id}
|
||||||
- ${slug}
|
- ${slug}
|
||||||
- ${url}
|
- ${url}
|
||||||
|
- ${servings}
|
||||||
|
- ${yieldQuantity}
|
||||||
|
- ${yieldText}
|
||||||
|
|
||||||
To add, modify, or delete Recipe Actions, visit the Data Management page (more on that below).
|
To add, modify, or delete Recipe Actions, visit the Data Management page (more on that below).
|
||||||
|
|
||||||
|
|||||||
@@ -31,27 +31,27 @@
|
|||||||
|
|
||||||
### Database
|
### Database
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
| --------------------- | :------: | ----------------------------------------------------------------------- |
|
| ------------------------------------------------------- | :------: | ----------------------------------------------------------------------- |
|
||||||
| DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' |
|
| DB_ENGINE | sqlite | Optional: 'sqlite', 'postgres' |
|
||||||
| POSTGRES_USER | mealie | Postgres database user |
|
| POSTGRES_USER<super>[†][secrets]</super> | mealie | Postgres database user |
|
||||||
| POSTGRES_PASSWORD | mealie | Postgres database password |
|
| POSTGRES_PASSWORD<super>[†][secrets]</super> | mealie | Postgres database password |
|
||||||
| POSTGRES_SERVER | postgres | Postgres database server address |
|
| POSTGRES_SERVER<super>[†][secrets]</super> | postgres | Postgres database server address |
|
||||||
| POSTGRES_PORT | 5432 | Postgres database port |
|
| POSTGRES_PORT<super>[†][secrets]</super> | 5432 | Postgres database port |
|
||||||
| POSTGRES_DB | mealie | Postgres database name |
|
| POSTGRES_DB<super>[†][secrets]</super> | mealie | Postgres database name |
|
||||||
| POSTGRES_URL_OVERRIDE | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables |
|
| POSTGRES_URL_OVERRIDE<super>[†][secrets]</super> | None | Optional Postgres URL override to use instead of POSTGRES\_\* variables |
|
||||||
|
|
||||||
### Email
|
### Email
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
| ------------------ | :-----: | ------------------------------------------------- |
|
| ----------------------------------------------- | :-----: | ------------------------------------------------- |
|
||||||
| SMTP_HOST | None | Required For email |
|
| SMTP_HOST<super>[†][secrets]</super> | None | Required For email |
|
||||||
| SMTP_PORT | 587 | Required For email |
|
| SMTP_PORT<super>[†][secrets]</super> | 587 | Required For email |
|
||||||
| SMTP_FROM_NAME | Mealie | Required For email |
|
| SMTP_FROM_NAME | Mealie | Required For email |
|
||||||
| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' |
|
| SMTP_AUTH_STRATEGY | TLS | Required For email, Options: 'TLS', 'SSL', 'NONE' |
|
||||||
| SMTP_FROM_EMAIL | None | Required For email |
|
| SMTP_FROM_EMAIL | None | Required For email |
|
||||||
| SMTP_USER | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
|
| SMTP_USER<super>[†][secrets]</super> | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
|
||||||
| SMTP_PASSWORD | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
|
| SMTP_PASSWORD<super>[†][secrets]</super> | None | Required if SMTP_AUTH_STRATEGY is 'TLS' or 'SSL' |
|
||||||
|
|
||||||
### Webworker
|
### Webworker
|
||||||
|
|
||||||
@@ -72,21 +72,21 @@ Use this only when mealie is run without a webserver or reverse proxy.
|
|||||||
|
|
||||||
### LDAP
|
### LDAP
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
| -------------------- | :-----: | ----------------------------------------------------------------------------------------------------------------------------------- |
|
| ----------------------------------------------------- | :-----: | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
|
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
|
||||||
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap://ldap.example.com) |
|
| LDAP_SERVER_URL<super>[†][secrets]</super> | None | LDAP server URL (e.g. ldap://ldap.example.com) |
|
||||||
| LDAP_TLS_INSECURE | False | Do not verify server certificate when using secure LDAP |
|
| LDAP_TLS_INSECURE | False | Do not verify server certificate when using secure LDAP |
|
||||||
| LDAP_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
| LDAP_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
||||||
| LDAP_ENABLE_STARTTLS | False | Optional. Use STARTTLS to connect to the server |
|
| LDAP_ENABLE_STARTTLS | False | Optional. Use STARTTLS to connect to the server |
|
||||||
| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) |
|
| LDAP_BASE_DN | None | Starting point when searching for users authentication (e.g. `CN=Users,DC=xx,DC=yy,DC=de`) |
|
||||||
| LDAP_QUERY_BIND | None | Optional bind user for LDAP search queries (e.g. `cn=admin,cn=users,dc=example,dc=com`). If `None` then anonymous bind will be used |
|
| LDAP_QUERY_BIND | None | Optional bind user for LDAP search queries (e.g. `cn=admin,cn=users,dc=example,dc=com`). If `None` then anonymous bind will be used |
|
||||||
| LDAP_QUERY_PASSWORD | None | Optional password for the bind user used in LDAP_QUERY_BIND |
|
| LDAP_QUERY_PASSWORD<super>[†][secrets]</super> | None | Optional password for the bind user used in LDAP_QUERY_BIND |
|
||||||
| LDAP_USER_FILTER | None | Optional LDAP filter to narrow down eligible users (e.g. `(memberOf=cn=mealie_user,dc=example,dc=com)`) |
|
| LDAP_USER_FILTER | None | Optional LDAP filter to narrow down eligible users (e.g. `(memberOf=cn=mealie_user,dc=example,dc=com)`) |
|
||||||
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
|
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
|
||||||
| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id |
|
| LDAP_ID_ATTRIBUTE | uid | The LDAP attribute that maps to the user's id |
|
||||||
| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name |
|
| LDAP_NAME_ATTRIBUTE | name | The LDAP attribute that maps to the user's name |
|
||||||
| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email |
|
| LDAP_MAIL_ATTRIBUTE | mail | The LDAP attribute that maps to the user's email |
|
||||||
|
|
||||||
### OpenID Connect (OIDC)
|
### OpenID Connect (OIDC)
|
||||||
|
|
||||||
@@ -94,23 +94,22 @@ Use this only when mealie is run without a webserver or reverse proxy.
|
|||||||
|
|
||||||
For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
|
For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
|---------------------------------------------------|:-------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
| ----------------------------------------------------------------------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect |
|
| OIDC_AUTH_ENABLED | False | Enables authentication via OpenID Connect |
|
||||||
| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC |
|
| OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC |
|
||||||
| OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration |
|
| OIDC_CONFIGURATION_URL<super>[†][secrets]</super> | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration |
|
||||||
| OIDC_CLIENT_ID | None | The client id of your configured client in your provider |
|
| OIDC_CLIENT_ID<super>[†][secrets]</super> | None | The client id of your configured client in your provider |
|
||||||
| OIDC_CLIENT_SECRET <br/> :octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider |
|
| OIDC_CLIENT_SECRET<super>[†][secrets]</super> <br/> :octicons-tag-24: v2.0.0 | None | The client secret of your configured client in your provider |
|
||||||
| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
| OIDC_USER_GROUP | None | If specified, only users belonging to this group will be able to successfully authenticate, regardless of the `OIDC_ADMIN_GROUP`. For more information see [this page](../authentication/oidc.md#groups) |
|
||||||
| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be able to successfully authenticate *and* be made an admin. For more information see [this page](../authentication/oidc-v2.md#groups) |
|
| OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be made an admin. For more information see [this page](../authentication/oidc.md#groups) |
|
||||||
| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed and you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL |
|
| OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed an you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL |
|
||||||
| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" |
|
| OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" |
|
||||||
| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked |
|
| OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked |
|
||||||
| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") |
|
| OIDC_SIGNING_ALGORITHM | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
||||||
| OIDC_NAME_CLAIM | name | This is the claim which Mealie will use for the users Full Name |
|
| OIDC_USER_CLAIM | email | This is the claim which Mealie will use to look up an existing user by (e.g. "email", "preferred_username") |
|
||||||
| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** |
|
| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** |
|
||||||
| OIDC_SCOPES_OVERRIDE | None | Advanced configuration used to override the scopes requested from the IdP. **Most users won't need to change this**. At a minimum, 'openid profile email' are required. |
|
| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
||||||
| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
|
|
||||||
|
|
||||||
### OpenAI
|
### OpenAI
|
||||||
|
|
||||||
@@ -119,17 +118,13 @@ 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).
|
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"}'`)
|
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 |
|
| Variables | Default | Description |
|
||||||
| ---------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------- |
|
||||||
| OPENAI_BASE_URL | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform |
|
| OPENAI_BASE_URL<super>[†][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 | None | Your OpenAI API Key. Enables OpenAI-related features |
|
| OPENAI_API_KEY<super>[†][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_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_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
|
||||||
| 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_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
|
||||||
| 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 | 60 | 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 |
|
|
||||||
|
|
||||||
### Theming
|
### Theming
|
||||||
|
|
||||||
@@ -154,24 +149,80 @@ Setting the following environmental variables will change the theme of the front
|
|||||||
|
|
||||||
### Docker Secrets
|
### Docker Secrets
|
||||||
|
|
||||||
Setting a credential can be done using secrets when running in a Docker container.
|
### Docker Secrets
|
||||||
This can be used to avoid leaking passwords through compose files, environment variables, or command-line history.
|
|
||||||
For example, to configure the Postgres database password in Docker compose, create a file on the host that contains only the password, and expose that file to the Mealie service as a secret with the correct name.
|
> <super>†</super> Starting in version `2.4.2`, any environment variable in the preceding lists with a dagger
|
||||||
Note that environment variables take priority over secrets, so any previously defined environment variables should be removed when migrating to secrets.
|
> symbol next to them support the Docker Compose secrets pattern, below.
|
||||||
|
[Docker Compose secrets][docker-secrets] can be used to secure sensitive information regarding the Mealie implementation
|
||||||
|
by managing control of each secret independently from the single `.env` file. This is helpful for users that may need
|
||||||
|
different levels of access for various, sensitive environment variables, such as differentiating between hardening
|
||||||
|
operations (e.g., server endpoints and ports) and user access control (e.g., usernames, passwords, and API keys).
|
||||||
|
|
||||||
|
To convert any of these environment variables to a Docker Compose secret, append `_FILE` to the environment variable and
|
||||||
|
connect it with a Docker Compose secret, per the [Docker documentation][docker-secrets].
|
||||||
|
|
||||||
|
If both the base environment variable and the secret pattern of the environment variable are set, the secret will always
|
||||||
|
take precedence.
|
||||||
|
|
||||||
|
For example, a user that wishes to harden their operations by only giving some access to their database URL, but who
|
||||||
|
wish to place additional security around their user access control, may have a Docker Compose configuration similar to:
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
mealie:
|
mealie:
|
||||||
...
|
|
||||||
environment:
|
|
||||||
...
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
secrets:
|
secrets:
|
||||||
- POSTGRES_PASSWORD
|
# These secrets will be loaded by Docker into the `/run/secrets` folder within the container.
|
||||||
|
- postgres-host
|
||||||
|
- postgres-port
|
||||||
|
- postgres-db-name
|
||||||
|
- postgres-user
|
||||||
|
- postgres-password
|
||||||
|
|
||||||
|
environment:
|
||||||
|
DB_ENGINE: postgres
|
||||||
|
POSTGRES_SERVER: duplicate.entry.tld # This will be ignored, due to the secret defined, below.
|
||||||
|
POSTGRES_SERVER_FILE: /run/secrets/postgres-host
|
||||||
|
POSTGRES_PORT_FILE: /run/secrets/postgres-port
|
||||||
|
POSTGRES_DB_FILE: /run/secrets/postgres-db-name
|
||||||
|
POSTGRES_USER_FILE: /run/secrets/postgres-user
|
||||||
|
POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password
|
||||||
|
|
||||||
|
# Each of these secrets are loaded via these local files. Different patterns are available. See the Docker Compose
|
||||||
|
# documentation for more information.
|
||||||
secrets:
|
secrets:
|
||||||
POSTGRES_PASSWORD:
|
postgres-host:
|
||||||
file: postgrespassword.txt
|
file: ./secrets/postgres-host.txt
|
||||||
|
postgres-port:
|
||||||
|
file: ./secrets/postgres-port.txt
|
||||||
|
postgres-db-name:
|
||||||
|
file: ./secrets/sensitive/postgres-db-name.txt
|
||||||
|
postgres-user:
|
||||||
|
file: ./secrets/sensitive/postgres-user.txt
|
||||||
|
postgres-password:
|
||||||
|
file: ./secrets/sensitive/postgres-password.txt
|
||||||
|
```
|
||||||
|
In the example above, a directory organization and access pattern may look like the following:
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── secrets # Access restricted to anyone that can manage secrets
|
||||||
|
├── postgres-host.txt
|
||||||
|
├── postgres-port.txt
|
||||||
|
└── sensitive # Access further-restricted to anyone managing service accounts
|
||||||
|
├── postgres-db-name.txt
|
||||||
|
├── postgres-password.txt
|
||||||
|
└── postgres-user.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
How you organize your secrets is ultimately up to you. At minimum, it's highly recommended to use secret patterns for
|
||||||
|
at least these sensitive environment variables when working within shared environments:
|
||||||
|
|
||||||
|
- `POSTGRES_PASSWORD`
|
||||||
|
- `SMTP_PASSWORD`
|
||||||
|
- `LDAP_QUERY_PASSWORD`
|
||||||
|
- `OPENAI_API_KEY`
|
||||||
|
|
||||||
|
[docker-secrets]: https://docs.docker.com/compose/use-secrets/
|
||||||
|
[secrets]: #docker-secrets
|
||||||
[unicorn_workers]: https://www.uvicorn.org/deployment/#built-in
|
[unicorn_workers]: https://www.uvicorn.org/deployment/#built-in
|
||||||
|
|||||||
@@ -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:
|
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!
|
1. Take a backup just in case!
|
||||||
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.4.2`
|
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.7.0`
|
||||||
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
|
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
|
4. Restart the container
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
mealie:
|
mealie:
|
||||||
image: ghcr.io/mealie-recipes/mealie:v2.4.2 # (3)
|
image: ghcr.io/mealie-recipes/mealie:v2.7.0 # (3)
|
||||||
container_name: mealie
|
container_name: mealie
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
mealie:
|
mealie:
|
||||||
image: ghcr.io/mealie-recipes/mealie:v2.4.2 # (3)
|
image: ghcr.io/mealie-recipes/mealie:v2.7.0 # (3)
|
||||||
container_name: mealie
|
container_name: mealie
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
# Backups and Restoring
|
# Backups and Restores
|
||||||
|
|
||||||
Mealie provides an integrated mechanics for doing full installation backups of the database. Navigate to `/admin/backups` to
|
Mealie provides an integrated mechanic for doing full installation backups of the database.
|
||||||
|
|
||||||
|
Navigate to Settings > Backups or manually by adding `/admin/backups` to your instance URL.
|
||||||
|
|
||||||
|
From this page, you will be able to:
|
||||||
|
|
||||||
- See a list of available backups
|
- See a list of available backups
|
||||||
- Perform a backups
|
- Create a backup
|
||||||
- Restore a backup
|
- Upload a backup
|
||||||
|
- Delete a backup (Confirmation Required)
|
||||||
|
- Download a backup
|
||||||
|
- Perform a restore
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
If you're using Mealie with SQLite all your data is stored in the /app/data/ folder in the container. You can easily perform entire site backups by stopping the container, and backing up this folder with your chosen tool. This is the **best** way to backup your data.
|
If you're using Mealie with SQLite all your data is stored in the /app/data/ folder in the container. You can easily perform entire site backups by stopping the container, and backing up this folder with your chosen tool. This is the **best** way to backup your data.
|
||||||
|
|
||||||
## Restoring from a Backup
|
## Restoring from a Backup
|
||||||
|
|
||||||
To restore from a backup it needs to be uploaded to your instance, this can be done through the web portal. On the lower left hand corner of the backups data table you'll see an upload button. Click this button and select the backup file you want to upload and it will be available to import shortly.
|
To restore from a backup it needs to be uploaded to your instance which can be done through the web portal. On the top left of the page you'll see an upload button. Click this button and select the backup file you want to upload and it will be available to import shortly. You can alternatively use one of the backups you see on the screen, if one exists.
|
||||||
|
|
||||||
Before importing it's critical that you understand the following:
|
Before importing it's critical that you understand the following:
|
||||||
|
|
||||||
@@ -19,6 +26,9 @@ Before importing it's critical that you understand the following:
|
|||||||
- This action cannot be undone
|
- This action cannot be undone
|
||||||
- If this action is successful you will be logged out and you will need to log back in to complete the restore
|
- If this action is successful you will be logged out and you will need to log back in to complete the restore
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If for some reason the restore does not succeed, you can review the logs of what the issue may be, download the backup .ZIP and edit the contents of database.json to potentially resolve the issue. For example, if you receive an error restoring 'shopping-list' you can edit out the contents of that list while allowing other sections to restore. If you would like any assistance on this, reach out over Discord.
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
Prior to beta-v5 using a mis-matched version of the database backup will result in an error that will prevent you from using the instance of Mealie requiring you to remove all data and reinstall. Post beta-v5 performing a mismatched restore will throw an error and alert the user of the issue.
|
Prior to beta-v5 using a mis-matched version of the database backup will result in an error that will prevent you from using the instance of Mealie requiring you to remove all data and reinstall. Post beta-v5 performing a mismatched restore will throw an error and alert the user of the issue.
|
||||||
|
|
||||||
|
|||||||
@@ -85,12 +85,13 @@ nav:
|
|||||||
- OpenID Connect: "documentation/getting-started/authentication/oidc-v2.md"
|
- OpenID Connect: "documentation/getting-started/authentication/oidc-v2.md"
|
||||||
|
|
||||||
- Community Guides:
|
- Community Guides:
|
||||||
|
- Bring API without internet exposure: "documentation/community-guide/bring-api.md"
|
||||||
|
- Automate Backups with n8n: "documentation/community-guide/n8n-backup-automation.md"
|
||||||
|
- Bulk Url Import: "documentation/community-guide/bulk-url-import.md"
|
||||||
|
- Home Assistant: "documentation/community-guide/home-assistant.md"
|
||||||
|
- Import Bookmarklet: "documentation/community-guide/import-recipe-bookmarklet.md"
|
||||||
- iOS Shortcuts: "documentation/community-guide/ios.md"
|
- iOS Shortcuts: "documentation/community-guide/ios.md"
|
||||||
- Reverse Proxy (SWAG): "documentation/community-guide/swag.md"
|
- Reverse Proxy (SWAG): "documentation/community-guide/swag.md"
|
||||||
- Home Assistant: "documentation/community-guide/home-assistant.md"
|
|
||||||
- Bulk Url Import: "documentation/community-guide/bulk-url-import.md"
|
|
||||||
- Import Bookmarklet: "documentation/community-guide/import-recipe-bookmarklet.md"
|
|
||||||
- Automate Backups with n8n: "documentation/community-guide/n8n-backup-automation.md"
|
|
||||||
|
|
||||||
- API Reference: "api/redoc.md"
|
- API Reference: "api/redoc.md"
|
||||||
|
|
||||||
@@ -98,6 +99,7 @@ nav:
|
|||||||
- Non-Code: "contributors/non-coders.md"
|
- Non-Code: "contributors/non-coders.md"
|
||||||
- Translating: "contributors/translating.md"
|
- Translating: "contributors/translating.md"
|
||||||
- Developers Guide:
|
- Developers Guide:
|
||||||
|
- Building Packages: "contributors/developers-guide/building-packages.md"
|
||||||
- Code Contributions: "contributors/developers-guide/code-contributions.md"
|
- Code Contributions: "contributors/developers-guide/code-contributions.md"
|
||||||
- Dev Getting Started: "contributors/developers-guide/starting-dev-server.md"
|
- Dev Getting Started: "contributors/developers-guide/starting-dev-server.md"
|
||||||
- Database Changes: "contributors/developers-guide/database-changes.md"
|
- Database Changes: "contributors/developers-guide/database-changes.md"
|
||||||
|
|||||||
@@ -236,14 +236,17 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function fetchRecipes(pageCount = 1) {
|
async function fetchRecipes(pageCount = 1) {
|
||||||
|
const orderDir = props.query?.orderDirection || preferences.value.orderDirection;
|
||||||
|
const orderByNullPosition = props.query?.orderByNullPosition || orderDir === "asc" ? "first" : "last";
|
||||||
return await fetchMore(
|
return await fetchMore(
|
||||||
page.value,
|
page.value,
|
||||||
perPage * pageCount,
|
perPage * pageCount,
|
||||||
props.query?.orderBy || preferences.value.orderBy,
|
props.query?.orderBy || preferences.value.orderBy,
|
||||||
props.query?.orderDirection || preferences.value.orderDirection,
|
orderDir,
|
||||||
|
orderByNullPosition,
|
||||||
props.query,
|
props.query,
|
||||||
// we use a computed queryFilter to filter out recipes that have a null value for the property we're sorting by
|
// we use a computed queryFilter to filter out recipes that have a null value for the property we're sorting by
|
||||||
queryFilter.value
|
queryFilter.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ export default defineComponent({
|
|||||||
const groupRecipeActionsStore = useGroupRecipeActions();
|
const groupRecipeActionsStore = useGroupRecipeActions();
|
||||||
|
|
||||||
async function executeRecipeAction(action: GroupRecipeActionOut) {
|
async function executeRecipeAction(action: GroupRecipeActionOut) {
|
||||||
const response = await groupRecipeActionsStore.execute(action, props.recipe);
|
const response = await groupRecipeActionsStore.execute(action, props.recipe, props.recipeScale);
|
||||||
|
|
||||||
if (action.actionType === "post") {
|
if (action.actionType === "post") {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
|
|||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
||||||
import { ShoppingListSummary } from "~/lib/api/types/household";
|
import { RecipeIngredient, ShoppingListAddRecipeParamsBulk, ShoppingListSummary } from "~/lib/api/types/household";
|
||||||
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export interface RecipeWithScale extends Recipe {
|
export interface RecipeWithScale extends Recipe {
|
||||||
scale: number;
|
scale: number;
|
||||||
@@ -342,12 +342,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addRecipesToList() {
|
async function addRecipesToList() {
|
||||||
const promises: Promise<any>[] = [];
|
if (!selectedShoppingList.value) {
|
||||||
recipeIngredientSections.value.forEach((section) => {
|
return;
|
||||||
if (!selectedShoppingList.value) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const recipeData: ShoppingListAddRecipeParamsBulk[] = [];
|
||||||
|
recipeIngredientSections.value.forEach((section) => {
|
||||||
const ingredients: RecipeIngredient[] = [];
|
const ingredients: RecipeIngredient[] = [];
|
||||||
section.ingredientSections.forEach((ingSection) => {
|
section.ingredientSections.forEach((ingSection) => {
|
||||||
ingSection.ingredients.forEach((ing) => {
|
ingSection.ingredients.forEach((ing) => {
|
||||||
@@ -361,24 +361,18 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(api.shopping.lists.addRecipe(
|
recipeData.push(
|
||||||
selectedShoppingList.value.id,
|
{
|
||||||
section.recipeId,
|
recipeId: section.recipeId,
|
||||||
section.recipeScale,
|
recipeIncrementQuantity: section.recipeScale,
|
||||||
ingredients,
|
recipeIngredients: ingredients,
|
||||||
));
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let success = true;
|
const { error } = await api.shopping.lists.addRecipes(selectedShoppingList.value.id, recipeData);
|
||||||
const results = await Promise.allSettled(promises);
|
error ? alert.error(i18n.tc("recipe.failed-to-add-recipes-to-list"))
|
||||||
results.forEach((result) => {
|
: alert.success(i18n.tc("recipe.successfully-added-to-list"));
|
||||||
if (result.status === "rejected") {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
success ? alert.success(i18n.tc("recipe.successfully-added-to-list"))
|
|
||||||
: alert.error(i18n.tc("failed-to-add-recipes-to-list"))
|
|
||||||
|
|
||||||
state.shoppingListDialog = false;
|
state.shoppingListDialog = false;
|
||||||
state.shoppingListIngredientDialog = false;
|
state.shoppingListIngredientDialog = false;
|
||||||
|
|||||||
@@ -86,29 +86,27 @@
|
|||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="d-flex justify-center flex-wrap">
|
<div v-if="lastMadeReady" class="d-flex justify-center flex-wrap">
|
||||||
<v-chip
|
<v-row no-gutters class="d-flex flex-wrap align-center" style="font-size: larger;">
|
||||||
label
|
<v-tooltip bottom>
|
||||||
:small="$vuetify.breakpoint.smAndDown"
|
<template #activator="{ on, attrs }">
|
||||||
color="accent custom-transparent"
|
<v-btn
|
||||||
class="ma-1 pa-3"
|
rounded
|
||||||
>
|
outlined
|
||||||
<v-icon left>
|
x-large
|
||||||
{{ $globals.icons.calendar }}
|
color="primary"
|
||||||
</v-icon>
|
v-bind="attrs"
|
||||||
<div v-if="lastMadeReady">
|
v-on="on"
|
||||||
{{ $t('recipe.last-made-date', { date: lastMade ? new Date(lastMade).toLocaleDateString($i18n.locale) : $t("general.never") } ) }}
|
@click="madeThisDialog = true"
|
||||||
</div>
|
>
|
||||||
<div v-else>
|
<v-icon left large>{{ $globals.icons.calendar }}</v-icon>
|
||||||
<AppLoader tiny />
|
<span class="text--secondary" style="letter-spacing: normal;"><b>{{ $tc("general.last-made") }}</b><br>{{ lastMade ? new Date(lastMade).toLocaleDateString($i18n.locale) : $tc("general.never") }}</span>
|
||||||
</div>
|
<v-icon right large>{{ $globals.icons.createAlt }}</v-icon>
|
||||||
</v-chip>
|
</v-btn>
|
||||||
</div>
|
</template>
|
||||||
<div class="d-flex justify-center flex-wrap mt-1">
|
<span>{{ $tc("recipe.made-this") }}</span>
|
||||||
<BaseButton :small="$vuetify.breakpoint.smAndDown" @click="madeThisDialog = true">
|
</v-tooltip>
|
||||||
<template #icon> {{ $globals.icons.chefHat }} </template>
|
</v-row>
|
||||||
{{ $t('recipe.made-this') }}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,15 +14,16 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-divider class="my-2" />
|
<v-divider class="my-2" />
|
||||||
<SafeMarkdown :source="recipe.description" />
|
<SafeMarkdown :source="recipe.description" />
|
||||||
<v-divider />
|
<v-divider v-if="recipe.description" />
|
||||||
<v-container class="d-flex flex-row flex-wrap justify-center align-center">
|
<v-container class="d-flex flex-row flex-wrap justify-center">
|
||||||
<div class="mx-5">
|
<div class="mx-6">
|
||||||
<v-row no-gutters class="mb-1">
|
<v-row no-gutters>
|
||||||
<v-col v-if="recipe.recipeYieldQuantity || recipe.recipeYield" cols="12" class="d-flex flex-wrap justify-center">
|
<v-col v-if="recipe.recipeYieldQuantity || recipe.recipeYield" cols="12" class="d-flex flex-wrap justify-center">
|
||||||
<RecipeYield
|
<RecipeYield
|
||||||
:yield-quantity="recipe.recipeYieldQuantity"
|
:yield-quantity="recipe.recipeYieldQuantity"
|
||||||
:yield="recipe.recipeYield"
|
:yield="recipe.recipeYield"
|
||||||
:scale="recipeScale"
|
:scale="recipeScale"
|
||||||
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -31,18 +32,18 @@
|
|||||||
<RecipeLastMade
|
<RecipeLastMade
|
||||||
v-if="isOwnGroup"
|
v-if="isOwnGroup"
|
||||||
:recipe="recipe"
|
:recipe="recipe"
|
||||||
:class="true ? undefined : 'force-bottom'"
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-5">
|
<div class="mx-6">
|
||||||
<RecipeTimeCard
|
<RecipeTimeCard
|
||||||
stacked
|
|
||||||
container-class="d-flex flex-wrap justify-center"
|
container-class="d-flex flex-wrap justify-center"
|
||||||
:prep-time="recipe.prepTime"
|
:prep-time="recipe.prepTime"
|
||||||
:total-time="recipe.totalTime"
|
:total-time="recipe.totalTime"
|
||||||
:perform-time="recipe.performTime"
|
:perform-time="recipe.performTime"
|
||||||
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|||||||
@@ -30,12 +30,17 @@
|
|||||||
<span v-html="recipeYield"></span>
|
<span v-html="recipeYield"></span>
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</div>
|
</div>
|
||||||
<RecipeTimeCard
|
<v-row class="d-flex justify-start">
|
||||||
:prep-time="recipe.prepTime"
|
<RecipeTimeCard
|
||||||
:total-time="recipe.totalTime"
|
:prep-time="recipe.prepTime"
|
||||||
:perform-time="recipe.performTime"
|
:total-time="recipe.totalTime"
|
||||||
color="white"
|
:perform-time="recipe.performTime"
|
||||||
/>
|
small
|
||||||
|
color="white"
|
||||||
|
class="ml-4"
|
||||||
|
/>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
<v-card-text v-if="preferences.showDescription" class="px-0">
|
<v-card-text v-if="preferences.showDescription" class="px-0">
|
||||||
<SafeMarkdown :source="recipe.description" />
|
<SafeMarkdown :source="recipe.description" />
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|||||||
@@ -1,41 +1,37 @@
|
|||||||
<template>
|
<template v-if="showCards">
|
||||||
<div v-if="stacked">
|
<div class="text-center">
|
||||||
<v-container>
|
<!-- Total Time -->
|
||||||
<v-row v-for="(time, index) in allTimes" :key="`${index}-stacked`" no-gutters>
|
<div v-if="validateTotalTime" class="time-card-flex mx-auto">
|
||||||
<v-col cols="12" :class="containerClass">
|
<v-row no-gutters class="d-flex flex-no-wrap align-center " :style="fontSize">
|
||||||
<v-chip
|
<v-icon :x-large="!small" left color="primary">
|
||||||
:small="$vuetify.breakpoint.smAndDown"
|
|
||||||
label
|
|
||||||
:color="color"
|
|
||||||
class="ma-1"
|
|
||||||
>
|
|
||||||
<v-icon left>
|
|
||||||
{{ $globals.icons.clockOutline }}
|
|
||||||
</v-icon>
|
|
||||||
{{ time.name }} |
|
|
||||||
{{ time.value }}
|
|
||||||
</v-chip>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<v-container :class="containerClass">
|
|
||||||
<v-chip
|
|
||||||
v-for="(time, index) in allTimes"
|
|
||||||
:key="index"
|
|
||||||
:small="$vuetify.breakpoint.smAndDown"
|
|
||||||
label
|
|
||||||
:color="color"
|
|
||||||
class="ma-1"
|
|
||||||
>
|
|
||||||
<v-icon left>
|
|
||||||
{{ $globals.icons.clockOutline }}
|
{{ $globals.icons.clockOutline }}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ time.name }} |
|
<p class="my-0"><span class="font-weight-bold">{{ validateTotalTime.name }}</span><br>{{ validateTotalTime.value }}</p>
|
||||||
{{ time.value }}
|
</v-row>
|
||||||
</v-chip>
|
</div>
|
||||||
</v-container>
|
<v-divider v-if="validateTotalTime && (validatePrepTime || validatePerformTime)" class="my-2" />
|
||||||
|
<!-- Prep Time & Perform Time -->
|
||||||
|
<div v-if="validatePrepTime || validatePerformTime" class="time-card-flex mx-auto">
|
||||||
|
<v-row
|
||||||
|
no-gutters
|
||||||
|
class="d-flex justify-center align-center" :class="{'flex-column': $vuetify.breakpoint.smAndDown}"
|
||||||
|
style="width: 100%;" :style="fontSize"
|
||||||
|
>
|
||||||
|
<div v-if="validatePrepTime" class="d-flex flex-no-wrap my-1">
|
||||||
|
<v-icon :large="!small" :dense="small" left color="primary">
|
||||||
|
{{ $globals.icons.knfife }}
|
||||||
|
</v-icon>
|
||||||
|
<p class="my-0"><span class="font-weight-bold">{{ validatePrepTime.name }}</span><br>{{ validatePrepTime.value }}</p>
|
||||||
|
</div>
|
||||||
|
<v-divider v-if="validatePrepTime && validatePerformTime" vertical class="mx-4" />
|
||||||
|
<div v-if="validatePerformTime" class="d-flex flex-no-wrap my-1">
|
||||||
|
<v-icon :large="!small" :dense="small" left color="primary">
|
||||||
|
{{ $globals.icons.potSteam }}
|
||||||
|
</v-icon>
|
||||||
|
<p class="my-0"><span class="font-weight-bold">{{ validatePerformTime.name }}</span><br>{{ validatePerformTime.value }}</p>
|
||||||
|
</div>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -44,10 +40,6 @@ import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
stacked: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
prepTime: {
|
prepTime: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
@@ -64,9 +56,9 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "accent custom-transparent"
|
default: "accent custom-transparent"
|
||||||
},
|
},
|
||||||
containerClass: {
|
small: {
|
||||||
type: String,
|
type: Boolean,
|
||||||
default: undefined,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
@@ -92,13 +84,16 @@ export default defineComponent({
|
|||||||
return !isEmpty(props.performTime) ? { name: i18n.t("recipe.perform-time"), value: props.performTime } : null;
|
return !isEmpty(props.performTime) ? { name: i18n.t("recipe.perform-time"), value: props.performTime } : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const allTimes = computed(() => {
|
const fontSize = computed(() => {
|
||||||
return [validateTotalTime.value, validatePrepTime.value, validatePerformTime.value].filter((x) => x !== null);
|
return props.small ? { fontSize: "smaller" } : { fontSize: "larger" };
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showCards,
|
showCards,
|
||||||
allTimes,
|
validateTotalTime,
|
||||||
|
validatePrepTime,
|
||||||
|
validatePerformTime,
|
||||||
|
fontSize,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="displayText" class="d-flex justify-space-between align-center">
|
<div v-if="scaledAmount" class="d-flex align-center">
|
||||||
<v-chip
|
<v-row no-gutters class="d-flex flex-wrap align-center" style="font-size: larger;">
|
||||||
:small="$vuetify.breakpoint.smAndDown"
|
<v-icon x-large left color="primary">
|
||||||
label
|
{{ $globals.icons.bread }}
|
||||||
:color="color"
|
|
||||||
>
|
|
||||||
<v-icon left>
|
|
||||||
{{ $globals.icons.potSteam }}
|
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<p class="my-0">
|
||||||
<span v-html="displayText"></span>
|
<span class="font-weight-bold">{{ $i18n.tc("recipe.yield") }}</span><br>
|
||||||
</v-chip>
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
|
<span v-html="scaledAmount"></span> {{ text }}
|
||||||
|
</p>
|
||||||
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import { useScaledAmount } from "~/composables/recipes/use-scaled-amount";
|
import { useScaledAmount } from "~/composables/recipes/use-scaled-amount";
|
||||||
|
|
||||||
@@ -39,7 +38,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { i18n } = useContext();
|
|
||||||
|
|
||||||
function sanitizeHTML(rawHtml: string) {
|
function sanitizeHTML(rawHtml: string) {
|
||||||
return DOMPurify.sanitize(rawHtml, {
|
return DOMPurify.sanitize(rawHtml, {
|
||||||
@@ -48,21 +46,15 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayText = computed(() => {
|
const scaledAmount = computed(() => {
|
||||||
if (!(props.yieldQuantity || props.yield)) {
|
const {scaledAmountDisplay} = useScaledAmount(props.yieldQuantity, props.scale);
|
||||||
return "";
|
return scaledAmountDisplay;
|
||||||
}
|
|
||||||
|
|
||||||
const { scaledAmountDisplay } = useScaledAmount(props.yieldQuantity, props.scale);
|
|
||||||
|
|
||||||
return i18n.t("recipe.yields-amount-with-text", {
|
|
||||||
amount: scaledAmountDisplay,
|
|
||||||
text: sanitizeHTML(props.yield),
|
|
||||||
}) as string;
|
|
||||||
});
|
});
|
||||||
|
const text = sanitizeHTML(props.yield);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
displayText,
|
scaledAmount,
|
||||||
|
text,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
:label="$t('shopping-list.note')"
|
:label="$t('shopping-list.note')"
|
||||||
rows="1"
|
rows="1"
|
||||||
auto-grow
|
auto-grow
|
||||||
|
autofocus
|
||||||
@keypress="handleNoteKeyPress"
|
@keypress="handleNoteKeyPress"
|
||||||
></v-textarea>
|
></v-textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,37 +81,37 @@
|
|||||||
<v-spacer />
|
<v-spacer />
|
||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
<v-card-actions class="ma-0 pt-0 pb-1 justify-end">
|
||||||
|
<BaseButtonGroup
|
||||||
|
:buttons="[
|
||||||
|
...(allowDelete ? [{
|
||||||
|
icon: $globals.icons.delete,
|
||||||
|
text: $t('general.delete'),
|
||||||
|
event: 'delete',
|
||||||
|
}] : []),
|
||||||
|
{
|
||||||
|
icon: $globals.icons.close,
|
||||||
|
text: $t('general.cancel'),
|
||||||
|
event: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.foods,
|
||||||
|
text: $t('shopping-list.toggle-food'),
|
||||||
|
event: 'toggle-foods',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.save,
|
||||||
|
text: $t('general.save'),
|
||||||
|
event: 'save',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@save="$emit('save')"
|
||||||
|
@cancel="$emit('cancel')"
|
||||||
|
@delete="$emit('delete')"
|
||||||
|
@toggle-foods="listItem.isFood = !listItem.isFood"
|
||||||
|
/>
|
||||||
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
<v-card-actions class="ma-0 pt-0 pb-1 justify-end">
|
|
||||||
<BaseButtonGroup
|
|
||||||
:buttons="[
|
|
||||||
{
|
|
||||||
icon: $globals.icons.delete,
|
|
||||||
text: $t('general.delete'),
|
|
||||||
event: 'delete',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.close,
|
|
||||||
text: $t('general.cancel'),
|
|
||||||
event: 'cancel',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.foods,
|
|
||||||
text: $t('shopping-list.toggle-food'),
|
|
||||||
event: 'toggle-foods',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.save,
|
|
||||||
text: $t('general.save'),
|
|
||||||
event: 'save',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
@save="$emit('save')"
|
|
||||||
@cancel="$emit('cancel')"
|
|
||||||
@delete="$emit('delete')"
|
|
||||||
@toggle-foods="listItem.isFood = !listItem.isFood"
|
|
||||||
/>
|
|
||||||
</v-card-actions>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -139,6 +140,11 @@ export default defineComponent({
|
|||||||
type: Array as () => IngredientFood[],
|
type: Array as () => IngredientFood[],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
allowDelete: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const foodStore = useFoodStore();
|
const foodStore = useFoodStore();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useAsync, useRouter, ref } from "@nuxtjs/composition-api";
|
|||||||
import { useAsyncKey } from "../use-utils";
|
import { useAsyncKey } from "../use-utils";
|
||||||
import { usePublicExploreApi } from "~/composables/api/api-client";
|
import { usePublicExploreApi } from "~/composables/api/api-client";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { Recipe } from "~/lib/api/types/recipe";
|
import { OrderByNullPosition, Recipe } from "~/lib/api/types/recipe";
|
||||||
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||||
|
|
||||||
export const allRecipes = ref<Recipe[]>([]);
|
export const allRecipes = ref<Recipe[]>([]);
|
||||||
@@ -11,12 +11,14 @@ export const recentRecipes = ref<Recipe[]>([]);
|
|||||||
function getParams(
|
function getParams(
|
||||||
orderBy: string | null = null,
|
orderBy: string | null = null,
|
||||||
orderDirection = "desc",
|
orderDirection = "desc",
|
||||||
|
orderByNullPosition: OrderByNullPosition | null = null,
|
||||||
query: RecipeSearchQuery | null = null,
|
query: RecipeSearchQuery | null = null,
|
||||||
queryFilter: string | null = null
|
queryFilter: string | null = null
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
orderBy,
|
orderBy,
|
||||||
orderDirection,
|
orderDirection,
|
||||||
|
orderByNullPosition,
|
||||||
paginationSeed: query?._searchSeed, // propagate searchSeed to stabilize random order pagination
|
paginationSeed: query?._searchSeed, // propagate searchSeed to stabilize random order pagination
|
||||||
searchSeed: query?._searchSeed, // unused, but pass it along for completeness of data
|
searchSeed: query?._searchSeed, // unused, but pass it along for completeness of data
|
||||||
search: query?.search,
|
search: query?.search,
|
||||||
@@ -47,6 +49,7 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
|||||||
perPage: number,
|
perPage: number,
|
||||||
orderBy: string | null = null,
|
orderBy: string | null = null,
|
||||||
orderDirection = "desc",
|
orderDirection = "desc",
|
||||||
|
orderByNullPosition: OrderByNullPosition | null = null,
|
||||||
query: RecipeSearchQuery | null = null,
|
query: RecipeSearchQuery | null = null,
|
||||||
queryFilter: string | null = null,
|
queryFilter: string | null = null,
|
||||||
) {
|
) {
|
||||||
@@ -54,7 +57,7 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
|||||||
const { data, error } = await api.recipes.getAll(
|
const { data, error } = await api.recipes.getAll(
|
||||||
page,
|
page,
|
||||||
perPage,
|
perPage,
|
||||||
getParams(orderBy, orderDirection, query, queryFilter),
|
getParams(orderBy, orderDirection, orderByNullPosition, query, queryFilter),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error?.response?.status === 404) {
|
if (error?.response?.status === 404) {
|
||||||
@@ -88,7 +91,7 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getRandom(query: RecipeSearchQuery | null = null, queryFilter: string | null = null) {
|
async function getRandom(query: RecipeSearchQuery | null = null, queryFilter: string | null = null) {
|
||||||
const { data } = await api.recipes.getAll(1, 1, getParams("random", "desc", query, queryFilter));
|
const { data } = await api.recipes.getAll(1, 1, getParams("random", "desc", null, query, queryFilter));
|
||||||
if (data?.items.length) {
|
if (data?.items.length) {
|
||||||
return data.items[0];
|
return data.items[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,17 +46,23 @@ export const useGroupRecipeActions = function (
|
|||||||
return groupRecipeActions.value;
|
return groupRecipeActions.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
function parseRecipeActionUrl(url: string, recipe: Recipe): string {
|
function parseRecipeActionUrl(url: string, recipe: Recipe, recipeScale: number): string {
|
||||||
|
const recipeServings = (recipe.recipeServings || 1) * recipeScale;
|
||||||
|
const recipeYieldQuantity = (recipe.recipeYieldQuantity || 1) * recipeScale;
|
||||||
|
|
||||||
/* eslint-disable no-template-curly-in-string */
|
/* eslint-disable no-template-curly-in-string */
|
||||||
return url
|
return url
|
||||||
.replace("${url}", window.location.href)
|
.replace("${url}", window.location.href)
|
||||||
.replace("${id}", recipe.id || "")
|
.replace("${id}", recipe.id || "")
|
||||||
.replace("${slug}", recipe.slug || "")
|
.replace("${slug}", recipe.slug || "")
|
||||||
|
.replace("${servings}", recipeServings.toString())
|
||||||
|
.replace("${yieldQuantity}", recipeYieldQuantity.toString())
|
||||||
|
.replace("${yieldText}", recipe.recipeYield || "")
|
||||||
/* eslint-enable no-template-curly-in-string */
|
/* eslint-enable no-template-curly-in-string */
|
||||||
};
|
};
|
||||||
|
|
||||||
async function execute(action: GroupRecipeActionOut, recipe: Recipe): Promise<void | RequestResponse<unknown>> {
|
async function execute(action: GroupRecipeActionOut, recipe: Recipe, recipeScale: number): Promise<void | RequestResponse<unknown>> {
|
||||||
const url = parseRecipeActionUrl(action.url, recipe);
|
const url = parseRecipeActionUrl(action.url, recipe, recipeScale);
|
||||||
|
|
||||||
switch (action.actionType) {
|
switch (action.actionType) {
|
||||||
case "link":
|
case "link":
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Ek het dit gemaak",
|
"made-this": "Ek het dit gemaak",
|
||||||
"how-did-it-turn-out": "Hoe het dit uitgedraai?",
|
"how-did-it-turn-out": "Hoe het dit uitgedraai?",
|
||||||
"user-made-this": "{user} het dit gemaak",
|
"user-made-this": "{user} het dit gemaak",
|
||||||
"last-made-date": "Laas gemaak {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Boodskap sleutel",
|
"message-key": "Boodskap sleutel",
|
||||||
"parse": "Verwerk",
|
"parse": "Verwerk",
|
||||||
|
|||||||
@@ -550,7 +550,7 @@
|
|||||||
"yields-amount-with-text": "Yields {amount} {text}",
|
"yields-amount-with-text": "Yields {amount} {text}",
|
||||||
"yield-text": "Yield Text",
|
"yield-text": "Yield Text",
|
||||||
"quantity": "Quantity",
|
"quantity": "Quantity",
|
||||||
"choose-unit": "Choose Unit",
|
"choose-unit": "اختر الوحدة",
|
||||||
"press-enter-to-create": "Press Enter to Create",
|
"press-enter-to-create": "Press Enter to Create",
|
||||||
"choose-food": "Choose Food",
|
"choose-food": "Choose Food",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Сготвих рецептата",
|
"made-this": "Сготвих рецептата",
|
||||||
"how-did-it-turn-out": "Как се получи?",
|
"how-did-it-turn-out": "Как се получи?",
|
||||||
"user-made-this": "{user} направи това",
|
"user-made-this": "{user} направи това",
|
||||||
"last-made-date": "Последно приготвена на {date}",
|
|
||||||
"api-extras-description": "Екстрите за рецепти са ключова характеристика на Mealie API. Те Ви позволяват да създавате персонализирани JSON двойки ключ/стойност в рамките на рецепта, за да ги препращате към други приложения. Можете да използвате тези ключове, за да предоставите информация за задействане на автоматизация или персонализирани съобщения, за препращане към желаното от Вас устройство.",
|
"api-extras-description": "Екстрите за рецепти са ключова характеристика на Mealie API. Те Ви позволяват да създавате персонализирани JSON двойки ключ/стойност в рамките на рецепта, за да ги препращате към други приложения. Можете да използвате тези ключове, за да предоставите информация за задействане на автоматизация или персонализирани съобщения, за препращане към желаното от Вас устройство.",
|
||||||
"message-key": "Ключ на съобщението",
|
"message-key": "Ключ на съобщението",
|
||||||
"parse": "Анализирай",
|
"parse": "Анализирай",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Ho he fet",
|
"made-this": "Ho he fet",
|
||||||
"how-did-it-turn-out": "Com ha sortit?",
|
"how-did-it-turn-out": "Com ha sortit?",
|
||||||
"user-made-this": "{user} ha fet això",
|
"user-made-this": "{user} ha fet això",
|
||||||
"last-made-date": "Última vegada feta {date}",
|
|
||||||
"api-extras-description": "Els extres de receptes són una funcionalitat clau de l'API de Mealie. Permeten crear parells clau/valor JSON personalitzats dins una recepta, per referenciar-los des d'aplicacions de tercers. Pots emprar aquestes claus per proveir informació, per exemple per a desencadenar automatitzacions o missatges personlitzats per a propagar al teu dispositiu desitjat.",
|
"api-extras-description": "Els extres de receptes són una funcionalitat clau de l'API de Mealie. Permeten crear parells clau/valor JSON personalitzats dins una recepta, per referenciar-los des d'aplicacions de tercers. Pots emprar aquestes claus per proveir informació, per exemple per a desencadenar automatitzacions o missatges personlitzats per a propagar al teu dispositiu desitjat.",
|
||||||
"message-key": "Clau del missatge",
|
"message-key": "Clau del missatge",
|
||||||
"parse": "Analitzar",
|
"parse": "Analitzar",
|
||||||
|
|||||||
@@ -349,7 +349,7 @@
|
|||||||
"note-only": "Pouze poznámka",
|
"note-only": "Pouze poznámka",
|
||||||
"random-meal": "Náhodné jídlo",
|
"random-meal": "Náhodné jídlo",
|
||||||
"random-dinner": "Náhodná večeře",
|
"random-dinner": "Náhodná večeře",
|
||||||
"random-side": "Random Side",
|
"random-side": "Náhodná příloha",
|
||||||
"this-rule-will-apply": "Toto pravidlo se použije {dayCriteria} {mealTypeCriteria}.",
|
"this-rule-will-apply": "Toto pravidlo se použije {dayCriteria} {mealTypeCriteria}.",
|
||||||
"to-all-days": "na všechny dny",
|
"to-all-days": "na všechny dny",
|
||||||
"on-days": "on {0}s",
|
"on-days": "on {0}s",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Toto jsem uvařil",
|
"made-this": "Toto jsem uvařil",
|
||||||
"how-did-it-turn-out": "Jak to dopadlo?",
|
"how-did-it-turn-out": "Jak to dopadlo?",
|
||||||
"user-made-this": "{user} udělal toto",
|
"user-made-this": "{user} udělal toto",
|
||||||
"last-made-date": "Naposledy uvařeno {date}",
|
|
||||||
"api-extras-description": "Recepty jsou klíčovým rysem rozhraní pro API Mealie. Umožňují vytvářet vlastní klíče/hodnoty JSON v rámci receptu pro odkazy na aplikace třetích stran. Tyto klíče můžete použít pro poskytnutí informací, například pro aktivaci automatizace nebo vlastních zpráv pro přenos do požadovaného zařízení.",
|
"api-extras-description": "Recepty jsou klíčovým rysem rozhraní pro API Mealie. Umožňují vytvářet vlastní klíče/hodnoty JSON v rámci receptu pro odkazy na aplikace třetích stran. Tyto klíče můžete použít pro poskytnutí informací, například pro aktivaci automatizace nebo vlastních zpráv pro přenos do požadovaného zařízení.",
|
||||||
"message-key": "Klíč zprávy",
|
"message-key": "Klíč zprávy",
|
||||||
"parse": "Analyzovat",
|
"parse": "Analyzovat",
|
||||||
@@ -1059,14 +1058,14 @@
|
|||||||
"food-label": "Označení jídla",
|
"food-label": "Označení jídla",
|
||||||
"edit-food": "Upravit jídlo",
|
"edit-food": "Upravit jídlo",
|
||||||
"food-data": "Data jídla",
|
"food-data": "Data jídla",
|
||||||
"example-food-singular": "ex: Onion",
|
"example-food-singular": "např.: Brambora",
|
||||||
"example-food-plural": "ex: Onions",
|
"example-food-plural": "např.: Brambory",
|
||||||
"label-overwrite-warning": "Toto přiřadí vybraný štítek všem vybraným jídlům a může přepsat stávající štítky.",
|
"label-overwrite-warning": "Toto přiřadí vybraný štítek všem vybraným jídlům a může přepsat stávající štítky.",
|
||||||
"on-hand-checkbox-label": "Nastavením tohoto příznaku bude tato potravina při přidávání receptu do nákupního seznamu ve výchozím nastavení odškrtnuta."
|
"on-hand-checkbox-label": "Nastavením tohoto příznaku bude tato potravina při přidávání receptu do nákupního seznamu ve výchozím nastavení odškrtnuta."
|
||||||
},
|
},
|
||||||
"units": {
|
"units": {
|
||||||
"seed-dialog-text": "Naplnit databázi s běžnými jednotkami používanými ve vašem jazyce.",
|
"seed-dialog-text": "Naplnit databázi s běžnými jednotkami používanými ve vašem jazyce.",
|
||||||
"combine-unit-description": "Combining the selected units will merge the Source Unit and Target Unit into a single unit. The {source-unit-will-be-deleted} and all of the references to the Source Unit will be updated to point to the Target Unit.",
|
"combine-unit-description": "Zkombinování zvolených jednotek spojí zdrojovou a cílovou jednotku do jedné. {source-unit-will-be-deleted} a všechny odkazy na ni budou upraveny na cílovou jednotku.",
|
||||||
"combine-unit": "Kombinovaná jednotka",
|
"combine-unit": "Kombinovaná jednotka",
|
||||||
"source-unit": "Zdrojová jednotka",
|
"source-unit": "Zdrojová jednotka",
|
||||||
"target-unit": "Cílová jednotka",
|
"target-unit": "Cílová jednotka",
|
||||||
@@ -1081,10 +1080,10 @@
|
|||||||
"unit-data": "Data jednotky",
|
"unit-data": "Data jednotky",
|
||||||
"use-abbv": "Používat zkratky",
|
"use-abbv": "Používat zkratky",
|
||||||
"fraction": "Zlomek",
|
"fraction": "Zlomek",
|
||||||
"example-unit-singular": "ex: Tablespoon",
|
"example-unit-singular": "např.: Čajová lžička",
|
||||||
"example-unit-plural": "ex: Tablespoons",
|
"example-unit-plural": "např.: Čajové lžičky",
|
||||||
"example-unit-abbreviation-singular": "ex: Tbsp",
|
"example-unit-abbreviation-singular": "např.: čl",
|
||||||
"example-unit-abbreviation-plural": "ex: Tbsps"
|
"example-unit-abbreviation-plural": "např.: čl"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"seed-dialog-text": "Naplnit databázi s běžnými popisky používanými ve vašem jazyce.",
|
"seed-dialog-text": "Naplnit databázi s běžnými popisky používanými ve vašem jazyce.",
|
||||||
@@ -1296,7 +1295,7 @@
|
|||||||
"profile": {
|
"profile": {
|
||||||
"welcome-user": "👋 Vítejte, {0}!",
|
"welcome-user": "👋 Vítejte, {0}!",
|
||||||
"description": "Spravujte svůj profil, recepty a nastavení skupiny.",
|
"description": "Spravujte svůj profil, recepty a nastavení skupiny.",
|
||||||
"invite-link": "Invite Link",
|
"invite-link": "Odkaz pozvánky",
|
||||||
"get-invite-link": "Získat odkaz na pozvánku",
|
"get-invite-link": "Získat odkaz na pozvánku",
|
||||||
"get-public-link": "Získat veřejný odkaz",
|
"get-public-link": "Získat veřejný odkaz",
|
||||||
"account-summary": "Přehled účtu",
|
"account-summary": "Přehled účtu",
|
||||||
@@ -1346,7 +1345,7 @@
|
|||||||
"cookbook": {
|
"cookbook": {
|
||||||
"cookbooks": "Kuchařky",
|
"cookbooks": "Kuchařky",
|
||||||
"description": "Kuchařky jsou dalším způsobem, jak uspořádat recepty vytvořením průřezů receptů, organizátorů a dalších filtrů. Vytvořením kuchařky se přidá položka na postranní panel a v kuchařce se zobrazí všechny recepty s vybranými filtry.",
|
"description": "Kuchařky jsou dalším způsobem, jak uspořádat recepty vytvořením průřezů receptů, organizátorů a dalších filtrů. Vytvořením kuchařky se přidá položka na postranní panel a v kuchařce se zobrazí všechny recepty s vybranými filtry.",
|
||||||
"hide-cookbooks-from-other-households": "Hide Cookbooks from Other Households",
|
"hide-cookbooks-from-other-households": "Skrýt kuchařky ostatních domácností",
|
||||||
"hide-cookbooks-from-other-households-description": "Pokud je povoleno, objeví se na postranním panelu pouze kuchařské knihy z vaší domácnosti",
|
"hide-cookbooks-from-other-households-description": "Pokud je povoleno, objeví se na postranním panelu pouze kuchařské knihy z vaší domácnosti",
|
||||||
"public-cookbook": "Veřejná kuchařka",
|
"public-cookbook": "Veřejná kuchařka",
|
||||||
"public-cookbook-description": "Veřejné kuchařky mohou být sdíleny s neregistrovanými uživateli a budou zobrazeny na stránce vaší skupiny.",
|
"public-cookbook-description": "Veřejné kuchařky mohou být sdíleny s neregistrovanými uživateli a budou zobrazeny na stránce vaší skupiny.",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Jeg har lavet denne",
|
"made-this": "Jeg har lavet denne",
|
||||||
"how-did-it-turn-out": "Hvordan blev det?",
|
"how-did-it-turn-out": "Hvordan blev det?",
|
||||||
"user-made-this": "{user} lavede denne",
|
"user-made-this": "{user} lavede denne",
|
||||||
"last-made-date": "Sidst tilberedt den {date}",
|
|
||||||
"api-extras-description": "Opskrifter ekstra er en central feature i Mealie API. De giver dig mulighed for at oprette brugerdefinerede JSON nøgle / værdi par inden for en opskrift, at henvise til fra 3. parts applikationer. Du kan bruge disse nøgler til at give oplysninger, for eksempel til at udløse automatiseringer eller brugerdefinerede beskeder til at videresende til din ønskede enhed.",
|
"api-extras-description": "Opskrifter ekstra er en central feature i Mealie API. De giver dig mulighed for at oprette brugerdefinerede JSON nøgle / værdi par inden for en opskrift, at henvise til fra 3. parts applikationer. Du kan bruge disse nøgler til at give oplysninger, for eksempel til at udløse automatiseringer eller brugerdefinerede beskeder til at videresende til din ønskede enhed.",
|
||||||
"message-key": "Beskednøgle",
|
"message-key": "Beskednøgle",
|
||||||
"parse": "Behandl data",
|
"parse": "Behandl data",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Ich hab's gemacht",
|
"made-this": "Ich hab's gemacht",
|
||||||
"how-did-it-turn-out": "Wie ist es geworden?",
|
"how-did-it-turn-out": "Wie ist es geworden?",
|
||||||
"user-made-this": "{user} hat's gemacht",
|
"user-made-this": "{user} hat's gemacht",
|
||||||
"last-made-date": "Zuletzt gemacht {date}",
|
|
||||||
"api-extras-description": "Rezepte-Extras sind ein Hauptmerkmal der Mealie API. Sie ermöglichen es dir, benutzerdefinierte JSON Key-Value-Paare zu einem Rezept zu erstellen, um Drittanbieter-Anwendungen zu steuern. Du kannst diese dazu verwenden, um Automatisierungen auszulösen oder benutzerdefinierte Nachrichten an bestimmte Geräte zu senden.",
|
"api-extras-description": "Rezepte-Extras sind ein Hauptmerkmal der Mealie API. Sie ermöglichen es dir, benutzerdefinierte JSON Key-Value-Paare zu einem Rezept zu erstellen, um Drittanbieter-Anwendungen zu steuern. Du kannst diese dazu verwenden, um Automatisierungen auszulösen oder benutzerdefinierte Nachrichten an bestimmte Geräte zu senden.",
|
||||||
"message-key": "Nachrichten-Schlüssel",
|
"message-key": "Nachrichten-Schlüssel",
|
||||||
"parse": "Parsen",
|
"parse": "Parsen",
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
"wednesday": "Τετάρτη",
|
"wednesday": "Τετάρτη",
|
||||||
"yes": "Ναι",
|
"yes": "Ναι",
|
||||||
"foods": "Τρόφιμα",
|
"foods": "Τρόφιμα",
|
||||||
"units": "Μονάδες",
|
"units": "Μονάδες μέτρησης",
|
||||||
"back": "Πίσω",
|
"back": "Πίσω",
|
||||||
"next": "Επόμενο",
|
"next": "Επόμενο",
|
||||||
"start": "Εναρξη",
|
"start": "Εναρξη",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Το έφτιαξα",
|
"made-this": "Το έφτιαξα",
|
||||||
"how-did-it-turn-out": "Ποιό ήταν το αποτέλεσμα;",
|
"how-did-it-turn-out": "Ποιό ήταν το αποτέλεσμα;",
|
||||||
"user-made-this": "Ο/η {user} το έφτιαξε αυτό",
|
"user-made-this": "Ο/η {user} το έφτιαξε αυτό",
|
||||||
"last-made-date": "Τελευταία παρασκευή {date}",
|
|
||||||
"api-extras-description": "Τα extras συνταγών αποτελούν βασικό χαρακτηριστικό του Mealie API. Σας επιτρέπουν να δημιουργήσετε προσαρμοσμένα ζεύγη κλειδιού/τιμής JSON μέσα σε μια συνταγή, να παραπέμψετε σε εφαρμογές τρίτων. Μπορείτε να χρησιμοποιήσετε αυτά τα κλειδιά για την παροχή πληροφοριών, για παράδειγμα πυροδότηση αυτοματισμών ή μετάδοση προσαρμοσμένων μηνυμάτων στη συσκευή που επιθυμείτε.",
|
"api-extras-description": "Τα extras συνταγών αποτελούν βασικό χαρακτηριστικό του Mealie API. Σας επιτρέπουν να δημιουργήσετε προσαρμοσμένα ζεύγη κλειδιού/τιμής JSON μέσα σε μια συνταγή, να παραπέμψετε σε εφαρμογές τρίτων. Μπορείτε να χρησιμοποιήσετε αυτά τα κλειδιά για την παροχή πληροφοριών, για παράδειγμα πυροδότηση αυτοματισμών ή μετάδοση προσαρμοσμένων μηνυμάτων στη συσκευή που επιθυμείτε.",
|
||||||
"message-key": "Κλειδί Μηνύματος",
|
"message-key": "Κλειδί Μηνύματος",
|
||||||
"parse": "Ανάλυση",
|
"parse": "Ανάλυση",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -469,7 +469,7 @@
|
|||||||
"categories": "Categorías",
|
"categories": "Categorías",
|
||||||
"cholesterol-content": "Colesterol",
|
"cholesterol-content": "Colesterol",
|
||||||
"comment-action": "Comentar",
|
"comment-action": "Comentar",
|
||||||
"comment": "Comentar",
|
"comment": "Comentario",
|
||||||
"comments": "Comentarios",
|
"comments": "Comentarios",
|
||||||
"delete-confirmation": "¿Estás seguro de eliminar esta receta?",
|
"delete-confirmation": "¿Estás seguro de eliminar esta receta?",
|
||||||
"delete-recipe": "Borrar receta",
|
"delete-recipe": "Borrar receta",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Lo hice",
|
"made-this": "Lo hice",
|
||||||
"how-did-it-turn-out": "¿Cómo resultó esto?",
|
"how-did-it-turn-out": "¿Cómo resultó esto?",
|
||||||
"user-made-this": "{user} hizo esto",
|
"user-made-this": "{user} hizo esto",
|
||||||
"last-made-date": "Cocinado por última vez el {date}",
|
|
||||||
"api-extras-description": "Los extras de las recetas son una característica clave de la API de Mealie. Permiten crear pares json clave/valor personalizados dentro de una receta para acceder desde aplicaciones de terceros. Puede utilizar estas claves para almacenar información, para activar la automatización o mensajes personalizados para transmitir al dispositivo deseado.",
|
"api-extras-description": "Los extras de las recetas son una característica clave de la API de Mealie. Permiten crear pares json clave/valor personalizados dentro de una receta para acceder desde aplicaciones de terceros. Puede utilizar estas claves para almacenar información, para activar la automatización o mensajes personalizados para transmitir al dispositivo deseado.",
|
||||||
"message-key": "Clave de mensaje",
|
"message-key": "Clave de mensaje",
|
||||||
"parse": "Analizar",
|
"parse": "Analizar",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Olen seda valmistanud",
|
"made-this": "Olen seda valmistanud",
|
||||||
"how-did-it-turn-out": "Kuidas tuli see välja?",
|
"how-did-it-turn-out": "Kuidas tuli see välja?",
|
||||||
"user-made-this": "{user} on seda valmistanud",
|
"user-made-this": "{user} on seda valmistanud",
|
||||||
"last-made-date": "Viimati valmistatud: {date}",
|
|
||||||
"api-extras-description": "Retsepti väljavõtted on Meali API oluline funktsioon. Neid saab kasutada kohandatud JSON-võtme/väärtuse paaride loomiseks retseptis, et viidata kolmandate osapoolte rakendustele. Neid klahve saab kasutada teabe edastamiseks, näiteks automaatse toimingu või kohandatud sõnumi käivitamiseks teie valitud seadmele.",
|
"api-extras-description": "Retsepti väljavõtted on Meali API oluline funktsioon. Neid saab kasutada kohandatud JSON-võtme/väärtuse paaride loomiseks retseptis, et viidata kolmandate osapoolte rakendustele. Neid klahve saab kasutada teabe edastamiseks, näiteks automaatse toimingu või kohandatud sõnumi käivitamiseks teie valitud seadmele.",
|
||||||
"message-key": "Sõnumi võti",
|
"message-key": "Sõnumi võti",
|
||||||
"parse": "Analüüsi",
|
"parse": "Analüüsi",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"support": "Tuki",
|
"support": "Tuki",
|
||||||
"version": "Versio",
|
"version": "Versio",
|
||||||
"unknown-version": "tuntematon",
|
"unknown-version": "tuntematon",
|
||||||
"sponsor": "Yhteistyökumppani"
|
"sponsor": "Sponsori"
|
||||||
},
|
},
|
||||||
"asset": {
|
"asset": {
|
||||||
"assets": "Liitteet",
|
"assets": "Liitteet",
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"enable-notifier": "Ota ilmoittaja käyttöön",
|
"enable-notifier": "Ota ilmoittaja käyttöön",
|
||||||
"what-events": "Mistä tapahtumista tulisi ilmoittaa?",
|
"what-events": "Mistä tapahtumista tulisi ilmoittaa?",
|
||||||
"user-events": "Käyttäjän tapahtumat",
|
"user-events": "Käyttäjän tapahtumat",
|
||||||
"mealplan-events": "Ateriasuunnittelun tapahtumat",
|
"mealplan-events": "Ateriasuunnitelman Tapahtumat",
|
||||||
"when-a-user-in-your-group-creates-a-new-mealplan": "Kun ryhmäsi käyttäjä tekee ateriasuunnitelman",
|
"when-a-user-in-your-group-creates-a-new-mealplan": "Kun ryhmäsi käyttäjä tekee ateriasuunnitelman",
|
||||||
"shopping-list-events": "Ostoslistatapahtumat",
|
"shopping-list-events": "Ostoslistatapahtumat",
|
||||||
"cookbook-events": "Keittokirjatapahtumat",
|
"cookbook-events": "Keittokirjatapahtumat",
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
"admin-group-management-text": "Muutokset tähän ryhmään tulevat näkymään välittömästi.",
|
"admin-group-management-text": "Muutokset tähän ryhmään tulevat näkymään välittömästi.",
|
||||||
"group-id-value": "Ryhmän tunniste: {0}",
|
"group-id-value": "Ryhmän tunniste: {0}",
|
||||||
"total-households": "Kotitaloudet Yhteensä",
|
"total-households": "Kotitaloudet Yhteensä",
|
||||||
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
|
"you-must-select-a-group-before-selecting-a-household": "Sinun tulee valita ryhmä ennen kuin valitset kotitalouden"
|
||||||
},
|
},
|
||||||
"household": {
|
"household": {
|
||||||
"household": "Kotitalous",
|
"household": "Kotitalous",
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
"save-recipe-before-use": "Tallenna resepti ennen käyttöä",
|
"save-recipe-before-use": "Tallenna resepti ennen käyttöä",
|
||||||
"section-title": "Osion otsikko",
|
"section-title": "Osion otsikko",
|
||||||
"servings": "Annokset",
|
"servings": "Annokset",
|
||||||
"serves-amount": "Serves {amount}",
|
"serves-amount": "{amount} annosta",
|
||||||
"share-recipe-message": "Halusin jakaa reseptin {0} kanssasi.",
|
"share-recipe-message": "Halusin jakaa reseptin {0} kanssasi.",
|
||||||
"show-nutrition-values": "Näytä ravintoarvot",
|
"show-nutrition-values": "Näytä ravintoarvot",
|
||||||
"sodium-content": "Natrium",
|
"sodium-content": "Natrium",
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
"no-recipe": "Ei reseptiä",
|
"no-recipe": "Ei reseptiä",
|
||||||
"locked-by-owner": "Omistajan lukitsema",
|
"locked-by-owner": "Omistajan lukitsema",
|
||||||
"join-the-conversation": "Liity keskusteluun",
|
"join-the-conversation": "Liity keskusteluun",
|
||||||
"add-recipe-to-mealplan": "Lisää resepti ateriasuunnitelmaan",
|
"add-recipe-to-mealplan": "Lisää resepti Ateriasuunnitelmaan",
|
||||||
"entry-type": "Merkinnän tyyppi",
|
"entry-type": "Merkinnän tyyppi",
|
||||||
"date-format-hint": "KK/PP/VVVV-muoto",
|
"date-format-hint": "KK/PP/VVVV-muoto",
|
||||||
"date-format-hint-yyyy-mm-dd": "VVVV-KK-PP-muoto",
|
"date-format-hint-yyyy-mm-dd": "VVVV-KK-PP-muoto",
|
||||||
@@ -546,9 +546,9 @@
|
|||||||
"failed-to-add-recipes-to-list": "Luetteloon lisääminen epäonnistui",
|
"failed-to-add-recipes-to-list": "Luetteloon lisääminen epäonnistui",
|
||||||
"failed-to-add-recipe-to-mealplan": "Reseptiä ei voitu lisätä ateriasuunnitelmaan",
|
"failed-to-add-recipe-to-mealplan": "Reseptiä ei voitu lisätä ateriasuunnitelmaan",
|
||||||
"failed-to-add-to-list": "Luetteloon lisääminen epäonnistui",
|
"failed-to-add-to-list": "Luetteloon lisääminen epäonnistui",
|
||||||
"yield": "Sato",
|
"yield": "Tuotto",
|
||||||
"yields-amount-with-text": "Yields {amount} {text}",
|
"yields-amount-with-text": "Tuottaa {amount} {text}",
|
||||||
"yield-text": "Yield Text",
|
"yield-text": "Tuotosteksti",
|
||||||
"quantity": "Määrä",
|
"quantity": "Määrä",
|
||||||
"choose-unit": "Valitse Yksikkö",
|
"choose-unit": "Valitse Yksikkö",
|
||||||
"press-enter-to-create": "Luo painamalla Enter",
|
"press-enter-to-create": "Luo painamalla Enter",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Tein tämän",
|
"made-this": "Tein tämän",
|
||||||
"how-did-it-turn-out": "Miten se onnistui?",
|
"how-did-it-turn-out": "Miten se onnistui?",
|
||||||
"user-made-this": "{user} teki tämän",
|
"user-made-this": "{user} teki tämän",
|
||||||
"last-made-date": "Viimeksi valmistettu {date}",
|
|
||||||
"api-extras-description": "Reseptiekstrat ovat Mealien API:n tärkeä ominaisuus. Niiden avulla voidaan luoda mukautettuja JSON-avain/arvo-pareja reseptin sisällä viitaten kolmannen osapuolen sovelluksiin. Näitä avaimia voi käyttää tiedon antamiseksi, esimerkiksi automaattisen toiminnon tai mukautetun viestin käynnistämiseksi haluamaasi laitteeseen.",
|
"api-extras-description": "Reseptiekstrat ovat Mealien API:n tärkeä ominaisuus. Niiden avulla voidaan luoda mukautettuja JSON-avain/arvo-pareja reseptin sisällä viitaten kolmannen osapuolen sovelluksiin. Näitä avaimia voi käyttää tiedon antamiseksi, esimerkiksi automaattisen toiminnon tai mukautetun viestin käynnistämiseksi haluamaasi laitteeseen.",
|
||||||
"message-key": "Viestiavain",
|
"message-key": "Viestiavain",
|
||||||
"parse": "Jäsennä",
|
"parse": "Jäsennä",
|
||||||
@@ -637,9 +636,9 @@
|
|||||||
"recipe-debugger-use-openai-description": "Käytä OpenAI:ta kaavinkirjaston sijaan tulosten jäsentämiseen. Luodessa reseptiä URL:n kautta tämä tehdään ilman eri kysymystä, kun kaavinkirjasto ei toimi, mutta voit kokeilla sitä tässä manuaalisesti.",
|
"recipe-debugger-use-openai-description": "Käytä OpenAI:ta kaavinkirjaston sijaan tulosten jäsentämiseen. Luodessa reseptiä URL:n kautta tämä tehdään ilman eri kysymystä, kun kaavinkirjasto ei toimi, mutta voit kokeilla sitä tässä manuaalisesti.",
|
||||||
"debug": "Vianhaku",
|
"debug": "Vianhaku",
|
||||||
"tree-view": "Puunäkymä",
|
"tree-view": "Puunäkymä",
|
||||||
"recipe-servings": "Recipe Servings",
|
"recipe-servings": "Reseptin annokset",
|
||||||
"recipe-yield": "Reseptin tekijä",
|
"recipe-yield": "Reseptin tuotto",
|
||||||
"recipe-yield-text": "Recipe Yield Text",
|
"recipe-yield-text": "Reseptin tuotosteksti",
|
||||||
"unit": "Yksikkö",
|
"unit": "Yksikkö",
|
||||||
"upload-image": "Lataa kuva",
|
"upload-image": "Lataa kuva",
|
||||||
"screen-awake": "Pidä näyttö aina päällä",
|
"screen-awake": "Pidä näyttö aina päällä",
|
||||||
@@ -661,25 +660,25 @@
|
|||||||
"missing-food": "Luo puuttuva ruoka: {food}",
|
"missing-food": "Luo puuttuva ruoka: {food}",
|
||||||
"no-food": "Ei ruokaa"
|
"no-food": "Ei ruokaa"
|
||||||
},
|
},
|
||||||
"reset-servings-count": "Reset Servings Count",
|
"reset-servings-count": "Palauta Annoksien Määrä",
|
||||||
"not-linked-ingredients": "Additional Ingredients"
|
"not-linked-ingredients": "Muut ainesosat"
|
||||||
},
|
},
|
||||||
"recipe-finder": {
|
"recipe-finder": {
|
||||||
"recipe-finder": "Recipe Finder",
|
"recipe-finder": "Reseptin etsijä",
|
||||||
"recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.",
|
"recipe-finder-description": "Etsi sopivia reseptejä saatavilla olevien ainesosien perusteella. Voit myös suodattaa tulokset saatavilla olevien ruoanvalmistusvälineiden perusteella, ja asettaa enimmäismäärän puuttuvia ainesosia tai välineitä.",
|
||||||
"selected-ingredients": "Selected Ingredients",
|
"selected-ingredients": "Valitut ainesosat",
|
||||||
"no-ingredients-selected": "No ingredients selected",
|
"no-ingredients-selected": "Ei valittuja ainesosia",
|
||||||
"missing": "Missing",
|
"missing": "Puuttuu",
|
||||||
"no-recipes-found": "No recipes found",
|
"no-recipes-found": "Reseptejä ei löytynyt",
|
||||||
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
"no-recipes-found-description": "Kokeile lisätä enemmän ainesosia hakuun tai säätää suodattimia",
|
||||||
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
"include-ingredients-on-hand": "Sisällytä saatavilla olevat ainesosat",
|
||||||
"include-tools-on-hand": "Include Tools On Hand",
|
"include-tools-on-hand": "Sisällytä saatavilla olevat välineet",
|
||||||
"max-missing-ingredients": "Max Missing Ingredients",
|
"max-missing-ingredients": "Puuttuvien ainesten enimmäismäärä",
|
||||||
"max-missing-tools": "Max Missing Tools",
|
"max-missing-tools": "Puuttuvien välineiden enimmäismäärä",
|
||||||
"selected-tools": "Selected Tools",
|
"selected-tools": "Valitut välineet",
|
||||||
"other-filters": "Other Filters",
|
"other-filters": "Muut suodattimet",
|
||||||
"ready-to-make": "Ready to Make",
|
"ready-to-make": "Valmis tekemään",
|
||||||
"almost-ready-to-make": "Almost Ready to Make"
|
"almost-ready-to-make": "Melkein valmis tekemään"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"advanced-search": "Tarkennettu haku",
|
"advanced-search": "Tarkennettu haku",
|
||||||
@@ -690,7 +689,7 @@
|
|||||||
"or": "Tai",
|
"or": "Tai",
|
||||||
"has-any": "On Mikä Tahansa",
|
"has-any": "On Mikä Tahansa",
|
||||||
"has-all": "On Kaikki",
|
"has-all": "On Kaikki",
|
||||||
"clear-selection": "Clear Selection",
|
"clear-selection": "Tyhjennä valinnat",
|
||||||
"results": "Tulokset",
|
"results": "Tulokset",
|
||||||
"search": "Hae",
|
"search": "Hae",
|
||||||
"search-mealie": "Hae Mealiestä (paina /)",
|
"search-mealie": "Hae Mealiestä (paina /)",
|
||||||
@@ -884,7 +883,7 @@
|
|||||||
"are-you-sure-you-want-to-check-all-items": "Haluatko varmasti valita kaikki kohteet?",
|
"are-you-sure-you-want-to-check-all-items": "Haluatko varmasti valita kaikki kohteet?",
|
||||||
"are-you-sure-you-want-to-uncheck-all-items": "Haluatko varmasti poistaa kaikki valinnat?",
|
"are-you-sure-you-want-to-uncheck-all-items": "Haluatko varmasti poistaa kaikki valinnat?",
|
||||||
"are-you-sure-you-want-to-delete-checked-items": "Haluatko varmasti poistaa kaikki valitut kohteet?",
|
"are-you-sure-you-want-to-delete-checked-items": "Haluatko varmasti poistaa kaikki valitut kohteet?",
|
||||||
"no-shopping-lists-found": "No Shopping Lists Found"
|
"no-shopping-lists-found": "Ostoslistoja ei löytynyt"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"all-recipes": "Reseptit",
|
"all-recipes": "Reseptit",
|
||||||
@@ -981,7 +980,7 @@
|
|||||||
"register": "Rekisteröidy",
|
"register": "Rekisteröidy",
|
||||||
"reset-password": "Palauta salasana",
|
"reset-password": "Palauta salasana",
|
||||||
"sign-in": "Kirjaudu",
|
"sign-in": "Kirjaudu",
|
||||||
"total-mealplans": "Ateriasuunnitelmia",
|
"total-mealplans": "Ateriasuunnitelma Yhteensä",
|
||||||
"total-users": "Käyttäjien määrä",
|
"total-users": "Käyttäjien määrä",
|
||||||
"upload-photo": "Tuo kuva",
|
"upload-photo": "Tuo kuva",
|
||||||
"use-8-characters-or-more-for-your-password": "Salasanan pituuden on oltava vähintään kahdeksan merkkiä",
|
"use-8-characters-or-more-for-your-password": "Salasanan pituuden on oltava vähintään kahdeksan merkkiä",
|
||||||
@@ -1030,7 +1029,7 @@
|
|||||||
"administrator": "Ylläpitäjä",
|
"administrator": "Ylläpitäjä",
|
||||||
"user-can-invite-other-to-group": "Käyttäjä voi kutsua muita ryhmään",
|
"user-can-invite-other-to-group": "Käyttäjä voi kutsua muita ryhmään",
|
||||||
"user-can-manage-group": "Käyttäjä voi hallita ryhmää",
|
"user-can-manage-group": "Käyttäjä voi hallita ryhmää",
|
||||||
"user-can-manage-household": "User can manage household",
|
"user-can-manage-household": "Käyttäjä voi hallita kotitaloutta",
|
||||||
"user-can-organize-group-data": "Käyttäjä voi järjestellä ryhmän tietoja",
|
"user-can-organize-group-data": "Käyttäjä voi järjestellä ryhmän tietoja",
|
||||||
"enable-advanced-features": "Salli edistyneemmät ominaisuudet",
|
"enable-advanced-features": "Salli edistyneemmät ominaisuudet",
|
||||||
"it-looks-like-this-is-your-first-time-logging-in": "Tämä vaikuttaa olevan ensimmäinen kirjautumisesi.",
|
"it-looks-like-this-is-your-first-time-logging-in": "Tämä vaikuttaa olevan ensimmäinen kirjautumisesi.",
|
||||||
@@ -1213,7 +1212,7 @@
|
|||||||
"result": "Valittu teksti tulee näkymään aiemmin valitun kentän sisällä."
|
"result": "Valittu teksti tulee näkymään aiemmin valitun kentän sisällä."
|
||||||
},
|
},
|
||||||
"pan-and-zoom-mode": "Kääntö- ja zoomaustila",
|
"pan-and-zoom-mode": "Kääntö- ja zoomaustila",
|
||||||
"pan-and-zoom-desc": "Select pan and zoom by clicking the icon. This mode allows to zoom inside the image and move around to make using big images easier.",
|
"pan-and-zoom-desc": "Valitse panorointi ja zoomaus klikkaamalla kuvaketta. Tämä tila mahdollistaa zoomauksen kuvan sisällä ja liikkumisen niin, että isojen kuvien käyttö on helpompaa.",
|
||||||
"split-text-mode": "Jaa tekstitila",
|
"split-text-mode": "Jaa tekstitila",
|
||||||
"split-modes": {
|
"split-modes": {
|
||||||
"line-mode": "Rivitila (oletus)",
|
"line-mode": "Rivitila (oletus)",
|
||||||
@@ -1232,37 +1231,37 @@
|
|||||||
"summary-title": "Yhteenveto",
|
"summary-title": "Yhteenveto",
|
||||||
"button-label-get-summary": "Hae Yhteenveto",
|
"button-label-get-summary": "Hae Yhteenveto",
|
||||||
"button-label-open-details": "Tiedot",
|
"button-label-open-details": "Tiedot",
|
||||||
"info-description-data-dir-size": "Datahakemiston Koko",
|
"info-description-data-dir-size": "Datahakemiston koko",
|
||||||
"info-description-log-file-size": "Lokitiedoston Koko",
|
"info-description-log-file-size": "Lokitiedoston Koko",
|
||||||
"info-description-cleanable-directories": "Siivottavat Hakemistot",
|
"info-description-cleanable-directories": "Siivottavat hakemistot",
|
||||||
"info-description-cleanable-images": "Siivottavat Kuvat",
|
"info-description-cleanable-images": "Siivottavat kuvat",
|
||||||
"storage": {
|
"storage": {
|
||||||
"title-temporary-directory": "Väliaikainen Hakemisto (.temp)",
|
"title-temporary-directory": "Väliaikainen hakemisto (.temp)",
|
||||||
"title-backups-directory": "Varmuuskopiointihakemisto (varmuuskopiot)",
|
"title-backups-directory": "Varmuuskopiointihakemisto (varmuuskopiot)",
|
||||||
"title-groups-directory": "Ryhmien Hakemisto (ryhmät)",
|
"title-groups-directory": "Ryhmien hakemisto (ryhmät)",
|
||||||
"title-recipes-directory": "Reseptihakemisto (reseptit)",
|
"title-recipes-directory": "Reseptihakemisto (reseptit)",
|
||||||
"title-user-directory": "Käyttäjähakemisto (käyttäjä)"
|
"title-user-directory": "Käyttäjähakemisto (käyttäjä)"
|
||||||
},
|
},
|
||||||
"action-delete-log-files-name": "Poista Lokitiedostot",
|
"action-delete-log-files-name": "Poista Lokitiedostot",
|
||||||
"action-delete-log-files-description": "Poistaa kaikki lokitiedostot",
|
"action-delete-log-files-description": "Poistaa kaikki lokitiedostot",
|
||||||
"action-clean-directories-name": "Puhdista Hakemistot",
|
"action-clean-directories-name": "Puhdista hakemistot",
|
||||||
"action-clean-directories-description": "Poistaa kaikki reseptikansiot, jotka eivät ole kelvollisia UUID-koodeja",
|
"action-clean-directories-description": "Poistaa kaikki reseptikansiot, jotka eivät ole kelvollisia UUID-koodeja",
|
||||||
"action-clean-temporary-files-name": "Poista väliaikaiset tiedostot",
|
"action-clean-temporary-files-name": "Poista väliaikaiset tiedostot",
|
||||||
"action-clean-temporary-files-description": "Poistaa kaikki tiedostot ja kansiot .temp kansiosta",
|
"action-clean-temporary-files-description": "Poistaa kaikki tiedostot ja kansiot .temp kansiosta",
|
||||||
"action-clean-images-name": "Puhdista Kuvat",
|
"action-clean-images-name": "Puhdista kuvat",
|
||||||
"action-clean-images-description": "Poistaa kaikki kuvat, jotka eivät pääty .webp",
|
"action-clean-images-description": "Poistaa kaikki kuvat, jotka eivät pääty .webp",
|
||||||
"actions-description": "Maintenance actions are {destructive_in_bold} and should be used with caution. Performing any of these actions is {irreversible_in_bold}.",
|
"actions-description": "Huoltotoimenpiteet ovat {destructive_in_bold} ja niitä tulisi käyttää varoen. Kaikkien näiden toimenpiteiden suorittaminen on {irreversible_in_bold}.",
|
||||||
"actions-description-destructive": "lopullinen",
|
"actions-description-destructive": "tuhoisia",
|
||||||
"actions-description-irreversible": "peruuttamaton",
|
"actions-description-irreversible": "peruuttamatonta",
|
||||||
"logs-action-refresh": "Päivitä lokit",
|
"logs-action-refresh": "Päivitä lokit",
|
||||||
"logs-page-title": "Mealie- Lokit",
|
"logs-page-title": "Mealie- Lokit",
|
||||||
"logs-tail-lines-label": "Tail Lines"
|
"logs-tail-lines-label": "Loppurivit"
|
||||||
},
|
},
|
||||||
"mainentance": {
|
"mainentance": {
|
||||||
"actions-title": "Toiminnot"
|
"actions-title": "Toiminnot"
|
||||||
},
|
},
|
||||||
"ingredients-natural-language-processor": "Ingredients Natural Language Processor",
|
"ingredients-natural-language-processor": "Ingredients Natural Language Processor",
|
||||||
"ingredients-natural-language-processor-explanation": "Mealie uses Conditional Random Fields (CRFs) for parsing and processing ingredients. The model used for ingredients is based off a data set of over 100,000 ingredients from a dataset compiled by the New York Times. Note that as the model is trained in English only, you may have varied results when using the model in other languages. This page is a playground for testing the model.",
|
"ingredients-natural-language-processor-explanation": "Mealie käyttää Conditional Random Fields (CRF) ainesosien jäsentämiseen ja prosessointiin. Ainesosien osalta käytetty malli perustuu yli 100000 ainesosan aineistoon New York Timesin kokoamasta aineistosta. Huomaa, että koska malli on koulutettu vain englanniksi, mallilla voi olla vaihtelevia tuloksia, kun käytät mallia muilla kielillä. Tämä sivu on mallin testaamiseen tarkoitettu leikkipaikka.",
|
||||||
"ingredients-natural-language-processor-explanation-2": "Se ei ole täydellinen, mutta se tuottaa hyviä tuloksia yleensä ja on hyvä lähtökohta manuaalisesti jäsentää ainesosia yksittäisiin kenttiin. Vaihtoehtoisesti voit myös käyttää Brute-prosessori, joka käyttää kuvion täsmäystekniikkaa tunnistamaan ainesosia.",
|
"ingredients-natural-language-processor-explanation-2": "Se ei ole täydellinen, mutta se tuottaa hyviä tuloksia yleensä ja on hyvä lähtökohta manuaalisesti jäsentää ainesosia yksittäisiin kenttiin. Vaihtoehtoisesti voit myös käyttää Brute-prosessori, joka käyttää kuvion täsmäystekniikkaa tunnistamaan ainesosia.",
|
||||||
"nlp": "NLP",
|
"nlp": "NLP",
|
||||||
"brute": "Brute",
|
"brute": "Brute",
|
||||||
@@ -1279,24 +1278,24 @@
|
|||||||
"setup": {
|
"setup": {
|
||||||
"first-time-setup": "Ensiasetukset",
|
"first-time-setup": "Ensiasetukset",
|
||||||
"welcome-to-mealie-get-started": "Tervetuloa Mealieen! Aloitetaan",
|
"welcome-to-mealie-get-started": "Tervetuloa Mealieen! Aloitetaan",
|
||||||
"already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage",
|
"already-set-up-bring-to-homepage": "Olen jo valmis, vie minut kotisivulle",
|
||||||
"common-settings-for-new-sites": "Here are some common settings for new sites",
|
"common-settings-for-new-sites": "Tässä muutamia yleisiä asetuksia uusille sivustoille",
|
||||||
"setup-complete": "Setup Complete!",
|
"setup-complete": "Asennus valmis.",
|
||||||
"here-are-a-few-things-to-help-you-get-started": "Näillä muutamilla asioilla pääset alkuun",
|
"here-are-a-few-things-to-help-you-get-started": "Näillä muutamilla asioilla pääset alkuun",
|
||||||
"restore-from-v1-backup": "Onko sinulla varmuuskopio aiemmasta Mealie v1 -instanssista? Palauta se tästä.",
|
"restore-from-v1-backup": "Onko sinulla varmuuskopio aiemmasta Mealie v1 -instanssista? Palauta se tästä.",
|
||||||
"manage-profile-or-get-invite-link": "Hallitse profiiliasi tai hanki kutsulinkki muille."
|
"manage-profile-or-get-invite-link": "Hallitse profiiliasi tai hanki kutsulinkki muille."
|
||||||
},
|
},
|
||||||
"debug-openai-services": "Debug OpenAI Services",
|
"debug-openai-services": "Debuggaa OpenAI-palveluita",
|
||||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
"debug-openai-services-description": "Käytä tätä sivua OpenAI-palveluiden debuggaamiseen. Voit testata OpenAI-yhteytesi ja nähdä tulokset täällä. Jos kuvapalvelut ovat käytössä, voit myös antaa kuvan.",
|
||||||
"run-test": "Run Test",
|
"run-test": "Suorita testi",
|
||||||
"test-results": "Test Results",
|
"test-results": "Testitulokset",
|
||||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
"group-delete-note": "Ryhmiä, joilla on käyttäjiä tai kotitalouksia, ei voi poistaa",
|
||||||
"household-delete-note": "Households with users cannot be deleted"
|
"household-delete-note": "Kotitalouksia, joissa on käyttäjiä, ei voi poistaa"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"welcome-user": "👋 Tervetuloa, {0}!",
|
"welcome-user": "👋 Tervetuloa, {0}!",
|
||||||
"description": "Hallitse profiiliasi, reseptejäsi ja ryhmäasetuksiasi.",
|
"description": "Hallitse profiiliasi, reseptejäsi ja ryhmäasetuksiasi.",
|
||||||
"invite-link": "Invite Link",
|
"invite-link": "",
|
||||||
"get-invite-link": "Hanki Kutsulinkki",
|
"get-invite-link": "Hanki Kutsulinkki",
|
||||||
"get-public-link": "Julkinen linkki",
|
"get-public-link": "Julkinen linkki",
|
||||||
"account-summary": "Tilin Yhteenveto",
|
"account-summary": "Tilin Yhteenveto",
|
||||||
@@ -1345,9 +1344,9 @@
|
|||||||
},
|
},
|
||||||
"cookbook": {
|
"cookbook": {
|
||||||
"cookbooks": "Keittokirjat",
|
"cookbooks": "Keittokirjat",
|
||||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
"description": "Keittokirjat ovat toinen tapa järjestää reseptejä luomalla poikkileikkauksia resepteistä, järjestäjistä, sekä muista suodattimista. Keittokirjan luominen lisää merkinnän sivupalkkiin, ja kaikki reseptit näkyvät keittokirjassa valittujen suodatinten mukaisesti.",
|
||||||
"hide-cookbooks-from-other-households": "Hide Cookbooks from Other Households",
|
"hide-cookbooks-from-other-households": "Piilota keittokirjat muista kotitalouksista",
|
||||||
"hide-cookbooks-from-other-households-description": "When enabled, only cookbooks from your household will appear on the sidebar",
|
"hide-cookbooks-from-other-households-description": "Kun käytössä, sivupalkissa näkyy vain oman kotitaloutesi keittokirjoja",
|
||||||
"public-cookbook": "Julkinen Keittokirja",
|
"public-cookbook": "Julkinen Keittokirja",
|
||||||
"public-cookbook-description": "Julkisia keittokirjoja voidaan jakaa ei-Mealien käyttäjille, ja ne näkyvät ryhmäsi sivulla.",
|
"public-cookbook-description": "Julkisia keittokirjoja voidaan jakaa ei-Mealien käyttäjille, ja ne näkyvät ryhmäsi sivulla.",
|
||||||
"filter-options": "Suodatuksen asetukset",
|
"filter-options": "Suodatuksen asetukset",
|
||||||
@@ -1363,25 +1362,25 @@
|
|||||||
},
|
},
|
||||||
"query-filter": {
|
"query-filter": {
|
||||||
"logical-operators": {
|
"logical-operators": {
|
||||||
"and": "AND",
|
"and": "JA",
|
||||||
"or": "OR"
|
"or": "TAI"
|
||||||
},
|
},
|
||||||
"relational-operators": {
|
"relational-operators": {
|
||||||
"equals": "equals",
|
"equals": "on yhtä kuin",
|
||||||
"does-not-equal": "does not equal",
|
"does-not-equal": "ei ole yhtä kuin",
|
||||||
"is-greater-than": "is greater than",
|
"is-greater-than": "on suurempi kuin",
|
||||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
"is-greater-than-or-equal-to": "on suurempi tai yhtäsuuri kuin",
|
||||||
"is-less-than": "is less than",
|
"is-less-than": "on vähemmän kuin",
|
||||||
"is-less-than-or-equal-to": "is less than or equal to"
|
"is-less-than-or-equal-to": "on vähemmän tai yhtäsuuri kuin"
|
||||||
},
|
},
|
||||||
"relational-keywords": {
|
"relational-keywords": {
|
||||||
"is": "is",
|
"is": "on",
|
||||||
"is-not": "is not",
|
"is-not": "ei ole",
|
||||||
"is-one-of": "is one of",
|
"is-one-of": "on yksi näistä",
|
||||||
"is-not-one-of": "is not one of",
|
"is-not-one-of": "ei ole yksi näistä",
|
||||||
"contains-all-of": "contains all of",
|
"contains-all-of": "sisältää kaikki nämä",
|
||||||
"is-like": "is like",
|
"is-like": "on kuin",
|
||||||
"is-not-like": "is not like"
|
"is-not-like": "ei ole kuin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Je l’ai cuisiné",
|
"made-this": "Je l’ai cuisiné",
|
||||||
"how-did-it-turn-out": "C’était bon ?",
|
"how-did-it-turn-out": "C’était bon ?",
|
||||||
"user-made-this": "{user} l’a cuisiné",
|
"user-made-this": "{user} l’a cuisiné",
|
||||||
"last-made-date": "Cuisiné le {date}",
|
|
||||||
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
||||||
"message-key": "Clé de message",
|
"message-key": "Clé de message",
|
||||||
"parse": "Analyser",
|
"parse": "Analyser",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Je l’ai cuisiné",
|
"made-this": "Je l’ai cuisiné",
|
||||||
"how-did-it-turn-out": "C’était bon ?",
|
"how-did-it-turn-out": "C’était bon ?",
|
||||||
"user-made-this": "{user} l’a cuisiné",
|
"user-made-this": "{user} l’a cuisiné",
|
||||||
"last-made-date": "Cuisiné le {date}",
|
|
||||||
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
||||||
"message-key": "Clé de message",
|
"message-key": "Clé de message",
|
||||||
"parse": "Analyser",
|
"parse": "Analyser",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Je l’ai cuisiné",
|
"made-this": "Je l’ai cuisiné",
|
||||||
"how-did-it-turn-out": "C’était bon ?",
|
"how-did-it-turn-out": "C’était bon ?",
|
||||||
"user-made-this": "{user} l’a cuisiné",
|
"user-made-this": "{user} l’a cuisiné",
|
||||||
"last-made-date": "Cuisiné le {date}",
|
|
||||||
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de l’API Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à l’appareil souhaité.",
|
||||||
"message-key": "Clé de message",
|
"message-key": "Clé de message",
|
||||||
"parse": "Analyser",
|
"parse": "Analyser",
|
||||||
|
|||||||
@@ -182,7 +182,7 @@
|
|||||||
"date": "Data",
|
"date": "Data",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"owner": "Dono",
|
"owner": "Dono",
|
||||||
"change-owner": "Change Owner",
|
"change-owner": "Mudar Proprietario",
|
||||||
"date-added": "Engadida o",
|
"date-added": "Engadida o",
|
||||||
"none": "Nada",
|
"none": "Nada",
|
||||||
"run": "Executar",
|
"run": "Executar",
|
||||||
@@ -214,10 +214,10 @@
|
|||||||
"confirm-delete-generic-items": "Estás seguro de que queres eliminar os seguintes elementos?",
|
"confirm-delete-generic-items": "Estás seguro de que queres eliminar os seguintes elementos?",
|
||||||
"organizers": "Organizadores",
|
"organizers": "Organizadores",
|
||||||
"caution": "Coidado",
|
"caution": "Coidado",
|
||||||
"show-advanced": "Show Advanced",
|
"show-advanced": "Mostrar Avanzadas",
|
||||||
"add-field": "Add Field",
|
"add-field": "Adicionar Campo",
|
||||||
"date-created": "Date Created",
|
"date-created": "Date Created",
|
||||||
"date-updated": "Date Updated"
|
"date-updated": "Data de Atualización"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"are-you-sure-you-want-to-delete-the-group": "Estás seguro de que queres eliminar <b>{groupName}<b/>?",
|
"are-you-sure-you-want-to-delete-the-group": "Estás seguro de que queres eliminar <b>{groupName}<b/>?",
|
||||||
@@ -295,11 +295,11 @@
|
|||||||
"private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings",
|
"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": "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",
|
"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",
|
"household-recipe-preferences": "Preferencias de receitas da casa",
|
||||||
"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.",
|
"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",
|
"allow-users-outside-of-your-household-to-see-your-recipes": "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",
|
"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"
|
"household-preferences": "Preferencias da Casa"
|
||||||
},
|
},
|
||||||
"meal-plan": {
|
"meal-plan": {
|
||||||
"create-a-new-meal-plan": "Crea un Novo Menú",
|
"create-a-new-meal-plan": "Crea un Novo Menú",
|
||||||
@@ -322,9 +322,9 @@
|
|||||||
"mealplan-update-failed": "Produciuse un erro na actualización do menú",
|
"mealplan-update-failed": "Produciuse un erro na actualización do menú",
|
||||||
"mealplan-updated": "Menú Actualizado",
|
"mealplan-updated": "Menú Actualizado",
|
||||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||||
"any-category": "Any Category",
|
"any-category": "Calquer Categoria",
|
||||||
"any-tag": "Any Tag",
|
"any-tag": "Calquer Etiqueta",
|
||||||
"any-household": "Any Household",
|
"any-household": "Calquer Casa",
|
||||||
"no-meal-plan-defined-yet": "Aínda non se definiu ningún menú",
|
"no-meal-plan-defined-yet": "Aínda non se definiu ningún menú",
|
||||||
"no-meal-planned-for-today": "Non hai ningunha comida prevista para hoxe",
|
"no-meal-planned-for-today": "Non hai ningunha comida prevista para hoxe",
|
||||||
"numberOfDays-hint": "Número de días ao cargar a páxina",
|
"numberOfDays-hint": "Número de días ao cargar a páxina",
|
||||||
@@ -395,7 +395,7 @@
|
|||||||
},
|
},
|
||||||
"tandoor": {
|
"tandoor": {
|
||||||
"description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.",
|
"description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.",
|
||||||
"title": "Tandoor Recipes"
|
"title": "Receitas do Tandoor"
|
||||||
},
|
},
|
||||||
"recipe-data-migrations": "Recipe Data Migrations",
|
"recipe-data-migrations": "Recipe Data Migrations",
|
||||||
"recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.",
|
"recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.",
|
||||||
@@ -404,8 +404,8 @@
|
|||||||
"tag-all-recipes": "Tag all recipes with {tag-name} tag",
|
"tag-all-recipes": "Tag all recipes with {tag-name} tag",
|
||||||
"nextcloud-text": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.",
|
"nextcloud-text": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.",
|
||||||
"chowdown-text": "Mealie admite de forma nativa o formato do repositorio de chowdown. Descarga o repositorio de códigos como ficheiro .zip e cárgao a continuación.",
|
"chowdown-text": "Mealie admite de forma nativa o formato do repositorio de chowdown. Descarga o repositorio de códigos como ficheiro .zip e cárgao a continuación.",
|
||||||
"recipe-1": "Recipe 1",
|
"recipe-1": "Receita 1",
|
||||||
"recipe-2": "Recipe 2",
|
"recipe-2": "Receita 2",
|
||||||
"paprika-text": "Mealie can import recipes from the Paprika application. Export your recipes from paprika, rename the export extension to .zip and upload it below.",
|
"paprika-text": "Mealie can import recipes from the Paprika application. Export your recipes from paprika, rename the export extension to .zip and upload it below.",
|
||||||
"mealie-text": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
|
"mealie-text": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
|
||||||
"plantoeat": {
|
"plantoeat": {
|
||||||
@@ -424,15 +424,15 @@
|
|||||||
"new-recipe": {
|
"new-recipe": {
|
||||||
"bulk-add": "Bulk Add",
|
"bulk-add": "Bulk Add",
|
||||||
"error-details": "Only websites containing ld+json or microdata can be imported by Mealie. Most major recipe websites support this data structure. If your site cannot be imported but there is json data in the log, please submit a github issue with the URL and data.",
|
"error-details": "Only websites containing ld+json or microdata can be imported by Mealie. Most major recipe websites support this data structure. If your site cannot be imported but there is json data in the log, please submit a github issue with the URL and data.",
|
||||||
"error-title": "Looks Like We Couldn't Find Anything",
|
"error-title": "Parece que non conseguimos encontrar nada",
|
||||||
"from-url": "Import a Recipe",
|
"from-url": "Importar unha Receita",
|
||||||
"github-issues": "GitHub Issues",
|
"github-issues": "Problemas no GitHub",
|
||||||
"google-ld-json-info": "Google ld+json Info",
|
"google-ld-json-info": "Google ld+json Info",
|
||||||
"must-be-a-valid-url": "Must be a Valid URL",
|
"must-be-a-valid-url": "Precisa ser un URL válido",
|
||||||
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Paste in your recipe data. Each line will be treated as an item in a list",
|
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Pegue os datos da sua receita. Cada liña será tratada como un item nunha lista",
|
||||||
"recipe-markup-specification": "Recipe Markup Specification",
|
"recipe-markup-specification": "Especificación Markup da Receita",
|
||||||
"recipe-url": "Recipe URL",
|
"recipe-url": "URL da Receita",
|
||||||
"recipe-html-or-json": "Recipe HTML or JSON",
|
"recipe-html-or-json": "Receita en HTML ou JSON",
|
||||||
"upload-a-recipe": "Upload a Recipe",
|
"upload-a-recipe": "Upload a Recipe",
|
||||||
"upload-individual-zip-file": "Upload an individual .zip file exported from another Mealie instance.",
|
"upload-individual-zip-file": "Upload an individual .zip file exported from another Mealie instance.",
|
||||||
"url-form-hint": "Copy and paste a link from your favorite recipe website",
|
"url-form-hint": "Copy and paste a link from your favorite recipe website",
|
||||||
@@ -440,13 +440,13 @@
|
|||||||
"trim-whitespace-description": "Trim leading and trailing whitespace as well as blank lines",
|
"trim-whitespace-description": "Trim leading and trailing whitespace as well as blank lines",
|
||||||
"trim-prefix-description": "Trim first character from each line",
|
"trim-prefix-description": "Trim first character from each line",
|
||||||
"split-by-numbered-line-description": "Attempts to split a paragraph by matching '1)' or '1.' patterns",
|
"split-by-numbered-line-description": "Attempts to split a paragraph by matching '1)' or '1.' patterns",
|
||||||
"import-by-url": "Import a recipe by URL",
|
"import-by-url": "Importar unha receita por URL",
|
||||||
"create-manually": "Create a recipe manually",
|
"create-manually": "Create a recipe manually",
|
||||||
"make-recipe-image": "Make this the recipe image"
|
"make-recipe-image": "Make this the recipe image"
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
"404-page-not-found": "404 Page not found",
|
"404-page-not-found": "404 Páxina non encontrada",
|
||||||
"all-recipes": "All Recipes",
|
"all-recipes": "Todas as receitas",
|
||||||
"new-page-created": "New page created",
|
"new-page-created": "New page created",
|
||||||
"page": "Páxina",
|
"page": "Páxina",
|
||||||
"page-creation-failed": "Produciuse un erro ao creala páxina",
|
"page-creation-failed": "Produciuse un erro ao creala páxina",
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
"calories-suffix": "calorías",
|
"calories-suffix": "calorías",
|
||||||
"carbohydrate-content": "Carbohidratos",
|
"carbohydrate-content": "Carbohidratos",
|
||||||
"categories": "Categorías",
|
"categories": "Categorías",
|
||||||
"cholesterol-content": "Cholesterol",
|
"cholesterol-content": "Colesterol",
|
||||||
"comment-action": "Comentar",
|
"comment-action": "Comentar",
|
||||||
"comment": "Comentario",
|
"comment": "Comentario",
|
||||||
"comments": "Comentarios",
|
"comments": "Comentarios",
|
||||||
@@ -535,7 +535,7 @@
|
|||||||
"add-recipe-to-mealplan": "Add Recipe to Mealplan",
|
"add-recipe-to-mealplan": "Add Recipe to Mealplan",
|
||||||
"entry-type": "Entry Type",
|
"entry-type": "Entry Type",
|
||||||
"date-format-hint": "Formato MM/DD/YYYY",
|
"date-format-hint": "Formato MM/DD/YYYY",
|
||||||
"date-format-hint-yyyy-mm-dd": "YYYY-MM-DD format",
|
"date-format-hint-yyyy-mm-dd": "Formato AAAA-MM-DD",
|
||||||
"add-to-list": "Add to List",
|
"add-to-list": "Add to List",
|
||||||
"add-to-plan": "Add to Plan",
|
"add-to-plan": "Add to Plan",
|
||||||
"add-to-timeline": "Add to Timeline",
|
"add-to-timeline": "Add to Timeline",
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
"choose-unit": "Choose Unit",
|
"choose-unit": "Choose Unit",
|
||||||
"press-enter-to-create": "Press Enter to Create",
|
"press-enter-to-create": "Press Enter to Create",
|
||||||
"choose-food": "Choose Food",
|
"choose-food": "Choose Food",
|
||||||
"notes": "Notes",
|
"notes": "Notas",
|
||||||
"toggle-section": "Toggle Section",
|
"toggle-section": "Toggle Section",
|
||||||
"see-original-text": "See Original Text",
|
"see-original-text": "See Original Text",
|
||||||
"original-text-with-value": "Original Text: {originalText}",
|
"original-text-with-value": "Original Text: {originalText}",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} fixo isto",
|
"user-made-this": "{user} fixo isto",
|
||||||
"last-made-date": "Feito por Última Vez {date}",
|
|
||||||
"api-extras-description": "Os extras de receitas son unha característica clave da API de Mealie. Permítenche crear pares de clave/valor JSON personalizados dentro dunha receita, para facer referencia desde aplicacións de terceiros. Podes usar estas teclas para proporcionar información, por exemplo, para activar automatizacións ou mensaxes personalizadas para transmitir ao dispositivo que desexes.",
|
"api-extras-description": "Os extras de receitas son unha característica clave da API de Mealie. Permítenche crear pares de clave/valor JSON personalizados dentro dunha receita, para facer referencia desde aplicacións de terceiros. Podes usar estas teclas para proporcionar información, por exemplo, para activar automatizacións ou mensaxes personalizadas para transmitir ao dispositivo que desexes.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
@@ -1194,8 +1193,8 @@
|
|||||||
"demo_password": "Password: {password}"
|
"demo_password": "Password: {password}"
|
||||||
},
|
},
|
||||||
"ocr-editor": {
|
"ocr-editor": {
|
||||||
"ocr-editor": "Ocr editor",
|
"ocr-editor": "Editor OCR",
|
||||||
"toolbar": "Toolbar",
|
"toolbar": "Barra de ferramentas",
|
||||||
"selection-mode": "Selection mode",
|
"selection-mode": "Selection mode",
|
||||||
"pan-and-zoom-picture": "Pan and zoom picture",
|
"pan-and-zoom-picture": "Pan and zoom picture",
|
||||||
"split-text": "Split text",
|
"split-text": "Split text",
|
||||||
@@ -1203,7 +1202,7 @@
|
|||||||
"split-by-block": "Split by text block",
|
"split-by-block": "Split by text block",
|
||||||
"flatten": "Flatten regardless of original formating",
|
"flatten": "Flatten regardless of original formating",
|
||||||
"help": {
|
"help": {
|
||||||
"help": "Help",
|
"help": "Axuda",
|
||||||
"mouse-modes": "Mouse modes",
|
"mouse-modes": "Mouse modes",
|
||||||
"selection-mode": "Selection Mode (default)",
|
"selection-mode": "Selection Mode (default)",
|
||||||
"selection-mode-desc": "The selection mode is the main mode that can be used to enter data:",
|
"selection-mode-desc": "The selection mode is the main mode that can be used to enter data:",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "הכנתי את זה",
|
"made-this": "הכנתי את זה",
|
||||||
"how-did-it-turn-out": "איך יצא?",
|
"how-did-it-turn-out": "איך יצא?",
|
||||||
"user-made-this": "{user} הכין את זה",
|
"user-made-this": "{user} הכין את זה",
|
||||||
"last-made-date": "נעשה לאחרונה ב{date}",
|
|
||||||
"api-extras-description": "מתכונים נוספים הם יכולת מפתח של Mealie API. הם מאפשרים ליצור צמדי key/value בצורת JSON על מנת לקרוא אותם בתוכנת צד שלישית. תוכלו להשתמש בצמדים האלה כדי לספק מידע, לדוגמא להפעיל אוטומציות או הודעות מותאמות אישית למכשירים מסויימים.",
|
"api-extras-description": "מתכונים נוספים הם יכולת מפתח של Mealie API. הם מאפשרים ליצור צמדי key/value בצורת JSON על מנת לקרוא אותם בתוכנת צד שלישית. תוכלו להשתמש בצמדים האלה כדי לספק מידע, לדוגמא להפעיל אוטומציות או הודעות מותאמות אישית למכשירים מסויימים.",
|
||||||
"message-key": "מפתח הודעה",
|
"message-key": "מפתח הודעה",
|
||||||
"parse": "ניתוח",
|
"parse": "ניתוח",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Napravio/la sam ovo",
|
"made-this": "Napravio/la sam ovo",
|
||||||
"how-did-it-turn-out": "Kako je ispalo?",
|
"how-did-it-turn-out": "Kako je ispalo?",
|
||||||
"user-made-this": "{user} je napravio/la ovo",
|
"user-made-this": "{user} je napravio/la ovo",
|
||||||
"last-made-date": "Zadnji put napravljeno {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Ključ poruke",
|
"message-key": "Ključ poruke",
|
||||||
"parse": "Razluči (parsiraj)",
|
"parse": "Razluči (parsiraj)",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Elkészítettem ezt",
|
"made-this": "Elkészítettem ezt",
|
||||||
"how-did-it-turn-out": "Hogyan sikerült?",
|
"how-did-it-turn-out": "Hogyan sikerült?",
|
||||||
"user-made-this": "ezt {user} készítette el",
|
"user-made-this": "ezt {user} készítette el",
|
||||||
"last-made-date": "Utoljára elkészítve {date}",
|
|
||||||
"api-extras-description": "A receptek extrái a Mealie API egyik legfontosabb szolgáltatása. Lehetővé teszik, hogy egyéni JSON kulcs/érték párokat hozzon létre egy receptben, amelyekre harmadik féltől származó alkalmazásokból hivatkozhat. Ezeket a kulcsokat információszolgáltatásra használhatja, például automatizmusok vagy egyéni üzenetek indítására, amelyeket a kívánt eszközre küldhet.",
|
"api-extras-description": "A receptek extrái a Mealie API egyik legfontosabb szolgáltatása. Lehetővé teszik, hogy egyéni JSON kulcs/érték párokat hozzon létre egy receptben, amelyekre harmadik féltől származó alkalmazásokból hivatkozhat. Ezeket a kulcsokat információszolgáltatásra használhatja, például automatizmusok vagy egyéni üzenetek indítására, amelyeket a kívánt eszközre küldhet.",
|
||||||
"message-key": "Üzenetkulcs",
|
"message-key": "Üzenetkulcs",
|
||||||
"parse": "Előkészítés",
|
"parse": "Előkészítés",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -517,7 +517,7 @@
|
|||||||
"saturated-fat-content": "Grassi saturi",
|
"saturated-fat-content": "Grassi saturi",
|
||||||
"save-recipe-before-use": "Salva la ricetta prima dell'uso",
|
"save-recipe-before-use": "Salva la ricetta prima dell'uso",
|
||||||
"section-title": "Titolo Sezione",
|
"section-title": "Titolo Sezione",
|
||||||
"servings": "Portate",
|
"servings": "Porzioni",
|
||||||
"serves-amount": "Porzioni {amount}",
|
"serves-amount": "Porzioni {amount}",
|
||||||
"share-recipe-message": "Volevo condividere la mia {0} ricetta con te.",
|
"share-recipe-message": "Volevo condividere la mia {0} ricetta con te.",
|
||||||
"show-nutrition-values": "Mostra Valori Nutrizionali",
|
"show-nutrition-values": "Mostra Valori Nutrizionali",
|
||||||
@@ -546,9 +546,9 @@
|
|||||||
"failed-to-add-recipes-to-list": "Impossibile aggiungere la ricetta alla lista",
|
"failed-to-add-recipes-to-list": "Impossibile aggiungere la ricetta alla lista",
|
||||||
"failed-to-add-recipe-to-mealplan": "Impossibile aggiungere la ricetta al piano alimentare",
|
"failed-to-add-recipe-to-mealplan": "Impossibile aggiungere la ricetta al piano alimentare",
|
||||||
"failed-to-add-to-list": "Errore durante l'aggiunta alla lista",
|
"failed-to-add-to-list": "Errore durante l'aggiunta alla lista",
|
||||||
"yield": "Porzioni",
|
"yield": "Quantità prodotta",
|
||||||
"yields-amount-with-text": "Rendimenti {amount} {text}",
|
"yields-amount-with-text": "Quantità prodotta: {amount} {text}",
|
||||||
"yield-text": "Testo di rendimento",
|
"yield-text": "Unità della quantità prodotta",
|
||||||
"quantity": "Quantità",
|
"quantity": "Quantità",
|
||||||
"choose-unit": "Scegli Unità",
|
"choose-unit": "Scegli Unità",
|
||||||
"press-enter-to-create": "Premi invio per creare",
|
"press-enter-to-create": "Premi invio per creare",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "L'Ho Preparato",
|
"made-this": "L'Ho Preparato",
|
||||||
"how-did-it-turn-out": "Come è venuto?",
|
"how-did-it-turn-out": "Come è venuto?",
|
||||||
"user-made-this": "{user} l'ha preparato",
|
"user-made-this": "{user} l'ha preparato",
|
||||||
"last-made-date": "Ultima Preparazione {date}",
|
|
||||||
"api-extras-description": "Le opzioni extra delle ricette sono una caratteristica fondamentale dell'API Mealie. Consentono di creare json personalizzati con coppie di chiavi/valore all'interno di una ricetta a cui fare riferimento tramite applicazioni terze. È possibile utilizzare queste chiavi per inserire informazioni, per esempio per attivare automazioni oppure per inoltrare messaggi personalizzati al dispositivo desiderato.",
|
"api-extras-description": "Le opzioni extra delle ricette sono una caratteristica fondamentale dell'API Mealie. Consentono di creare json personalizzati con coppie di chiavi/valore all'interno di una ricetta a cui fare riferimento tramite applicazioni terze. È possibile utilizzare queste chiavi per inserire informazioni, per esempio per attivare automazioni oppure per inoltrare messaggi personalizzati al dispositivo desiderato.",
|
||||||
"message-key": "Chiave Messaggio",
|
"message-key": "Chiave Messaggio",
|
||||||
"parse": "Analizza",
|
"parse": "Analizza",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "これを作りました",
|
"made-this": "これを作りました",
|
||||||
"how-did-it-turn-out": "どうなりましたか?",
|
"how-did-it-turn-out": "どうなりましたか?",
|
||||||
"user-made-this": "{user} がこれを作りました",
|
"user-made-this": "{user} がこれを作りました",
|
||||||
"last-made-date": "最後は {date} に作りました",
|
|
||||||
"api-extras-description": "レシピの追加機能はMealie APIの主な機能です。 サードパーティアプリから参照するために、レシピ内にカスタムJSONキー/値のペアを作成することができます。 これらのキーを使用して情報を提供することができます。例えば、自動化をトリガーしたり、カスタムメッセージをお使いのデバイスにリレーするなどです。",
|
"api-extras-description": "レシピの追加機能はMealie APIの主な機能です。 サードパーティアプリから参照するために、レシピ内にカスタムJSONキー/値のペアを作成することができます。 これらのキーを使用して情報を提供することができます。例えば、自動化をトリガーしたり、カスタムメッセージをお使いのデバイスにリレーするなどです。",
|
||||||
"message-key": "メッセージキー",
|
"message-key": "メッセージキー",
|
||||||
"parse": "解析",
|
"parse": "解析",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
"loading": "Kraunasi",
|
"loading": "Kraunasi",
|
||||||
"loading-events": "Užkrovimo įvykiai",
|
"loading-events": "Užkrovimo įvykiai",
|
||||||
"loading-recipe": "Receptai kraunasi...",
|
"loading-recipe": "Receptai kraunasi...",
|
||||||
"loading-ocr-data": "Loading OCR data...",
|
"loading-ocr-data": "Įkeliami OCR duomenys...",
|
||||||
"loading-recipes": "Receptai kraunasi",
|
"loading-recipes": "Receptai kraunasi",
|
||||||
"message": "Pranešimas",
|
"message": "Pranešimas",
|
||||||
"monday": "Pirmadienis",
|
"monday": "Pirmadienis",
|
||||||
@@ -210,14 +210,14 @@
|
|||||||
"upload-file": "Įkelti failą",
|
"upload-file": "Įkelti failą",
|
||||||
"created-on-date": "Sukurta: {0}",
|
"created-on-date": "Sukurta: {0}",
|
||||||
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
|
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
|
||||||
"clipboard-copy-failure": "Failed to copy to the clipboard.",
|
"clipboard-copy-failure": "Nepavyko nukopijuoti į iškarpinę.",
|
||||||
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
|
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
|
||||||
"organizers": "Organizers",
|
"organizers": "Organizers",
|
||||||
"caution": "Caution",
|
"caution": "Caution",
|
||||||
"show-advanced": "Show Advanced",
|
"show-advanced": "Rodyti plačiau",
|
||||||
"add-field": "Add Field",
|
"add-field": "Pridėti lauką",
|
||||||
"date-created": "Date Created",
|
"date-created": "Sukūrimo data",
|
||||||
"date-updated": "Date Updated"
|
"date-updated": "Atnaujinimo data"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"are-you-sure-you-want-to-delete-the-group": "Ar tikrai norite ištrinti <b>{groupName}<b/>?",
|
"are-you-sure-you-want-to-delete-the-group": "Ar tikrai norite ištrinti <b>{groupName}<b/>?",
|
||||||
@@ -322,7 +322,7 @@
|
|||||||
"mealplan-update-failed": "Mitybos plano atnaujinti nepavyko",
|
"mealplan-update-failed": "Mitybos plano atnaujinti nepavyko",
|
||||||
"mealplan-updated": "Mitybos planas atnaujintas",
|
"mealplan-updated": "Mitybos planas atnaujintas",
|
||||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
||||||
"any-category": "Any Category",
|
"any-category": "Bet kuri kategorija",
|
||||||
"any-tag": "Any Tag",
|
"any-tag": "Any Tag",
|
||||||
"any-household": "Any Household",
|
"any-household": "Any Household",
|
||||||
"no-meal-plan-defined-yet": "Nėra nustatytų mitybos planų",
|
"no-meal-plan-defined-yet": "Nėra nustatytų mitybos planų",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Aš tai gaminau",
|
"made-this": "Aš tai gaminau",
|
||||||
"how-did-it-turn-out": "Kaip tai pavyko?",
|
"how-did-it-turn-out": "Kaip tai pavyko?",
|
||||||
"user-made-this": "{user} gamino šį patiekalą",
|
"user-made-this": "{user} gamino šį patiekalą",
|
||||||
"last-made-date": "Paskutinį kartą gaminta {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Žinutės raktas",
|
"message-key": "Žinutės raktas",
|
||||||
"parse": "Nuskaityti",
|
"parse": "Nuskaityti",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Es to pagatavoju",
|
"made-this": "Es to pagatavoju",
|
||||||
"how-did-it-turn-out": "Kā tas izrādījās?",
|
"how-did-it-turn-out": "Kā tas izrādījās?",
|
||||||
"user-made-this": "{user}izdarīja šo",
|
"user-made-this": "{user}izdarīja šo",
|
||||||
"last-made-date": "Pēdējo reizi izgatavots {date}",
|
|
||||||
"api-extras-description": "Recepšu ekstras ir galvenā Mealie API iezīme. Tie ļauj jums izveidot pielāgotus JSON atslēgu/vērtību pārus receptē, lai atsaucotos no trešo pušu lietojumprogrammām. Varat izmantot šos taustiņus, lai sniegtu informāciju, piemēram, aktivizētu automatizāciju vai pielāgotus ziņojumus, lai tos pārsūtītu uz vēlamo ierīci.",
|
"api-extras-description": "Recepšu ekstras ir galvenā Mealie API iezīme. Tie ļauj jums izveidot pielāgotus JSON atslēgu/vērtību pārus receptē, lai atsaucotos no trešo pušu lietojumprogrammām. Varat izmantot šos taustiņus, lai sniegtu informāciju, piemēram, aktivizētu automatizāciju vai pielāgotus ziņojumus, lai tos pārsūtītu uz vēlamo ierīci.",
|
||||||
"message-key": "Ziņojuma atslēga",
|
"message-key": "Ziņojuma atslēga",
|
||||||
"parse": "Parsēšana",
|
"parse": "Parsēšana",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Ik heb dit gemaakt",
|
"made-this": "Ik heb dit gemaakt",
|
||||||
"how-did-it-turn-out": "Hoe was je gerecht?",
|
"how-did-it-turn-out": "Hoe was je gerecht?",
|
||||||
"user-made-this": "{user} heeft dit gemaakt",
|
"user-made-this": "{user} heeft dit gemaakt",
|
||||||
"last-made-date": "Laatst gemaakt op {date}",
|
|
||||||
"api-extras-description": "Extra's bij recepten zijn een belangrijke functie van de Mealie API. Hiermee kun je aangepaste JSON key/value paren maken bij een recept om naar te verwijzen vanuit applicaties van derden. Je kunt deze sleutels gebruiken om extra informatie te bieden, bijvoorbeeld om automatisering aan te sturen of aangepaste berichten naar je gewenste apparaat te laten versturen.",
|
"api-extras-description": "Extra's bij recepten zijn een belangrijke functie van de Mealie API. Hiermee kun je aangepaste JSON key/value paren maken bij een recept om naar te verwijzen vanuit applicaties van derden. Je kunt deze sleutels gebruiken om extra informatie te bieden, bijvoorbeeld om automatisering aan te sturen of aangepaste berichten naar je gewenste apparaat te laten versturen.",
|
||||||
"message-key": "Berichtsleutel",
|
"message-key": "Berichtsleutel",
|
||||||
"parse": "Ontleed",
|
"parse": "Ontleed",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Jeg har laget dette",
|
"made-this": "Jeg har laget dette",
|
||||||
"how-did-it-turn-out": "Hvordan ble det?",
|
"how-did-it-turn-out": "Hvordan ble det?",
|
||||||
"user-made-this": "{user} har laget dette",
|
"user-made-this": "{user} har laget dette",
|
||||||
"last-made-date": "Sist laget {date}",
|
|
||||||
"api-extras-description": "Ekstramaterialer til oppskrifter er en viktig funksjon i Mealie API-en. De lar deg opprette egendefinerte JSON-nøkkel/verdi-par innenfor en oppskrift for å referere fra tredjepartsapplikasjoner. Du kan bruke disse nøklene til å gi informasjon for eksempel for å utløse automatiseringer eller egendefinerte meldinger som skal videreformidles til ønsket enhet.",
|
"api-extras-description": "Ekstramaterialer til oppskrifter er en viktig funksjon i Mealie API-en. De lar deg opprette egendefinerte JSON-nøkkel/verdi-par innenfor en oppskrift for å referere fra tredjepartsapplikasjoner. Du kan bruke disse nøklene til å gi informasjon for eksempel for å utløse automatiseringer eller egendefinerte meldinger som skal videreformidles til ønsket enhet.",
|
||||||
"message-key": "Meldingsnøkkel",
|
"message-key": "Meldingsnøkkel",
|
||||||
"parse": "Analyser",
|
"parse": "Analyser",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Ugotowałem to",
|
"made-this": "Ugotowałem to",
|
||||||
"how-did-it-turn-out": "Jak się to udało?",
|
"how-did-it-turn-out": "Jak się to udało?",
|
||||||
"user-made-this": "{user} ugotował(a) to",
|
"user-made-this": "{user} ugotował(a) to",
|
||||||
"last-made-date": "Ostatnio ugotowano {date}",
|
|
||||||
"api-extras-description": "Dodatki w przepisach są kluczową cechą API Mealie. Pozwalają na tworzenie niestandardowych par kluczy/wartości JSON w przepisie do odwoływania się przez zewnętrzne aplikacje. Możesz użyć tych kluczy do wyzwalania automatyzacji lub przekazywania niestandardowych wiadomości do twoich wybranych urządzeń.",
|
"api-extras-description": "Dodatki w przepisach są kluczową cechą API Mealie. Pozwalają na tworzenie niestandardowych par kluczy/wartości JSON w przepisie do odwoływania się przez zewnętrzne aplikacje. Możesz użyć tych kluczy do wyzwalania automatyzacji lub przekazywania niestandardowych wiadomości do twoich wybranych urządzeń.",
|
||||||
"message-key": "Klucz Wiadomości",
|
"message-key": "Klucz Wiadomości",
|
||||||
"parse": "Analizuj",
|
"parse": "Analizuj",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"database-type": "Tipo do Banco de Dados",
|
"database-type": "Tipo do Banco de Dados",
|
||||||
"database-url": "URL do banco de dados",
|
"database-url": "URL do banco de dados",
|
||||||
"default-group": "Grupo Padrão",
|
"default-group": "Grupo Padrão",
|
||||||
"default-household": "Default Household",
|
"default-household": "Casa Padrão",
|
||||||
"demo": "Demonstração",
|
"demo": "Demonstração",
|
||||||
"demo-status": "Status da Demonstração",
|
"demo-status": "Status da Demonstração",
|
||||||
"development": "Desenvolvimento",
|
"development": "Desenvolvimento",
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
"date": "Data",
|
"date": "Data",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"owner": "Proprietário",
|
"owner": "Proprietário",
|
||||||
"change-owner": "Change Owner",
|
"change-owner": "Alterar Dono",
|
||||||
"date-added": "Data de inclusão",
|
"date-added": "Data de inclusão",
|
||||||
"none": "Nenhum",
|
"none": "Nenhum",
|
||||||
"run": "Executar",
|
"run": "Executar",
|
||||||
@@ -214,10 +214,10 @@
|
|||||||
"confirm-delete-generic-items": "Tem certeza que quer excluir os itens seguintes?",
|
"confirm-delete-generic-items": "Tem certeza que quer excluir os itens seguintes?",
|
||||||
"organizers": "Organizadores",
|
"organizers": "Organizadores",
|
||||||
"caution": "Cuidado",
|
"caution": "Cuidado",
|
||||||
"show-advanced": "Show Advanced",
|
"show-advanced": "Mostrar Avançado",
|
||||||
"add-field": "Add Field",
|
"add-field": "Adicionar Campo",
|
||||||
"date-created": "Date Created",
|
"date-created": "Data de Criação",
|
||||||
"date-updated": "Date Updated"
|
"date-updated": "Data de Atualização"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"are-you-sure-you-want-to-delete-the-group": "Tem certeza que deseja excluir o grupo <b>{groupName}<b/>?",
|
"are-you-sure-you-want-to-delete-the-group": "Tem certeza que deseja excluir o grupo <b>{groupName}<b/>?",
|
||||||
@@ -244,9 +244,9 @@
|
|||||||
"keep-my-recipes-private-description": "Define o seu grupo e todas as receitas padrão como privadas. Você sempre pode mudar isso mais tarde."
|
"keep-my-recipes-private-description": "Define o seu grupo e todas as receitas padrão como privadas. Você sempre pode mudar isso mais tarde."
|
||||||
},
|
},
|
||||||
"manage-members": "Gerenciar membros",
|
"manage-members": "Gerenciar membros",
|
||||||
"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": "Gerencie as permissões dos membros da sua casa. {manage} permite ao usuário acessar a página de gerenciamento de dados, e {invite} permite ao usuário gerar links de convites para outros usuários. Os proprietários de grupos não podem mudar suas próprias permissões.",
|
||||||
"manage": "Gerenciar",
|
"manage": "Gerenciar",
|
||||||
"manage-household": "Manage Household",
|
"manage-household": "Gerenciar Casa",
|
||||||
"invite": "Convidar",
|
"invite": "Convidar",
|
||||||
"looking-to-update-your-profile": "Procurando atualizar seu perfil?",
|
"looking-to-update-your-profile": "Procurando atualizar seu perfil?",
|
||||||
"default-recipe-preferences-description": "Estas são as configurações padrão quando uma nova receita é criada no seu grupo. Elas podem ser alteradas para receitas individuais no menu de configurações de receitas.",
|
"default-recipe-preferences-description": "Estas são as configurações padrão quando uma nova receita é criada no seu grupo. Elas podem ser alteradas para receitas individuais no menu de configurações de receitas.",
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
"disable-users-from-commenting-on-recipes": "Desabilitar usuários de comentar em receitas",
|
"disable-users-from-commenting-on-recipes": "Desabilitar usuários de comentar em receitas",
|
||||||
"disable-users-from-commenting-on-recipes-description": "Oculta a seção de comentários na página de receita e desativa os comentários",
|
"disable-users-from-commenting-on-recipes-description": "Oculta a seção de comentários na página de receita e desativa os comentários",
|
||||||
"disable-organizing-recipe-ingredients-by-units-and-food": "Desativar a organização de ingredientes de receita por unidades e alimentos",
|
"disable-organizing-recipe-ingredients-by-units-and-food": "Desativar a organização de ingredientes de receita por unidades e alimentos",
|
||||||
"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": "Oculta os campos Comida, Unidade e Quantidade de ingredientes e trata os ingredientes como campos de texto simples",
|
||||||
"general-preferences": "Preferências Gerais",
|
"general-preferences": "Preferências Gerais",
|
||||||
"group-recipe-preferences": "Preferências de Grupo de Receitas",
|
"group-recipe-preferences": "Preferências de Grupo de Receitas",
|
||||||
"report": "Denunciar",
|
"report": "Denunciar",
|
||||||
@@ -280,8 +280,8 @@
|
|||||||
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
|
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
|
||||||
},
|
},
|
||||||
"household": {
|
"household": {
|
||||||
"household": "Household",
|
"household": "Casa",
|
||||||
"households": "Households",
|
"households": "Casas",
|
||||||
"user-household": "User Household",
|
"user-household": "User Household",
|
||||||
"create-household": "Create Household",
|
"create-household": "Create Household",
|
||||||
"household-name": "Household Name",
|
"household-name": "Household Name",
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
"calories-suffix": "calorias",
|
"calories-suffix": "calorias",
|
||||||
"carbohydrate-content": "Carboidrato",
|
"carbohydrate-content": "Carboidrato",
|
||||||
"categories": "Categorias",
|
"categories": "Categorias",
|
||||||
"cholesterol-content": "Cholesterol",
|
"cholesterol-content": "Colesterol",
|
||||||
"comment-action": "Comentário",
|
"comment-action": "Comentário",
|
||||||
"comment": "Comentário",
|
"comment": "Comentário",
|
||||||
"comments": "Comentários",
|
"comments": "Comentários",
|
||||||
@@ -514,11 +514,11 @@
|
|||||||
"recipe-updated": "Receita atualizada",
|
"recipe-updated": "Receita atualizada",
|
||||||
"remove-from-favorites": "Remover dos Favoritos",
|
"remove-from-favorites": "Remover dos Favoritos",
|
||||||
"remove-section": "Remover Seção",
|
"remove-section": "Remover Seção",
|
||||||
"saturated-fat-content": "Saturated fat",
|
"saturated-fat-content": "Gorduras saturadas",
|
||||||
"save-recipe-before-use": "Salve a receita antes de utilizar",
|
"save-recipe-before-use": "Salve a receita antes de utilizar",
|
||||||
"section-title": "Título da Seção",
|
"section-title": "Título da Seção",
|
||||||
"servings": "Porções",
|
"servings": "Porções",
|
||||||
"serves-amount": "Serves {amount}",
|
"serves-amount": "Serve {amount}",
|
||||||
"share-recipe-message": "Eu quero compartilhar minha receita de {0} com você.",
|
"share-recipe-message": "Eu quero compartilhar minha receita de {0} com você.",
|
||||||
"show-nutrition-values": "Mostrar informações nutricionais",
|
"show-nutrition-values": "Mostrar informações nutricionais",
|
||||||
"sodium-content": "Sódio",
|
"sodium-content": "Sódio",
|
||||||
@@ -528,7 +528,7 @@
|
|||||||
"total-time": "Tempo Total",
|
"total-time": "Tempo Total",
|
||||||
"trans-fat-content": "Trans-fat",
|
"trans-fat-content": "Trans-fat",
|
||||||
"unable-to-delete-recipe": "Não foi possível apagar a receita",
|
"unable-to-delete-recipe": "Não foi possível apagar a receita",
|
||||||
"unsaturated-fat-content": "Unsaturated fat",
|
"unsaturated-fat-content": "Gorduras não saturadas",
|
||||||
"no-recipe": "Nenhuma Receita",
|
"no-recipe": "Nenhuma Receita",
|
||||||
"locked-by-owner": "Bloqueado pelo Proprietário",
|
"locked-by-owner": "Bloqueado pelo Proprietário",
|
||||||
"join-the-conversation": "Participe da conversa",
|
"join-the-conversation": "Participe da conversa",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Eu Fiz Isso",
|
"made-this": "Eu Fiz Isso",
|
||||||
"how-did-it-turn-out": "Como que ficou?",
|
"how-did-it-turn-out": "Como que ficou?",
|
||||||
"user-made-this": "{user} fez isso",
|
"user-made-this": "{user} fez isso",
|
||||||
"last-made-date": "Feito pela última vez em {date}",
|
|
||||||
"api-extras-description": "Extras de receitas são atributos-chave da API do Mealie. Assim, você pode criar pares chave/valor JSON personalizados dentro de uma receita, referenciando aplicações de terceiros. Você pode usar as chaves para fornecer informações, como por ex. ativar automações ou mensagens que serão enviadas a seus dispositivos.",
|
"api-extras-description": "Extras de receitas são atributos-chave da API do Mealie. Assim, você pode criar pares chave/valor JSON personalizados dentro de uma receita, referenciando aplicações de terceiros. Você pode usar as chaves para fornecer informações, como por ex. ativar automações ou mensagens que serão enviadas a seus dispositivos.",
|
||||||
"message-key": "Chave de mensagem",
|
"message-key": "Chave de mensagem",
|
||||||
"parse": "Analisar",
|
"parse": "Analisar",
|
||||||
@@ -600,9 +599,9 @@
|
|||||||
"create-recipe-from-an-image": "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.",
|
"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.",
|
"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-image": "Create from Image",
|
"create-from-image": "Criar a partir da imagem",
|
||||||
"should-translate-description": "Translate the recipe into my language",
|
"should-translate-description": "Traduza a receita para o meu idioma",
|
||||||
"please-wait-image-procesing": "Please wait, the image is processing. This may take some time.",
|
"please-wait-image-procesing": "Por favor aguarde, a imagem está sendo processada. Isto pode levar algum tempo.",
|
||||||
"bulk-url-import": "Importação de URL em massa",
|
"bulk-url-import": "Importação de URL em massa",
|
||||||
"debug-scraper": "Debug Scraper",
|
"debug-scraper": "Debug Scraper",
|
||||||
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crie uma receita fornecendo o nome. Todas as receitas devem ter nomes exclusivos.",
|
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crie uma receita fornecendo o nome. Todas as receitas devem ter nomes exclusivos.",
|
||||||
@@ -611,7 +610,7 @@
|
|||||||
"scrape-recipe-description": "Scrape uma receita por url. Forneça o Url para o site que você deseja scrape, e Mealie tentará raspar a receita desse site e adicioná-la à sua coleção.",
|
"scrape-recipe-description": "Scrape uma receita por url. Forneça o Url para o site que você deseja scrape, e Mealie tentará raspar a receita desse site e adicioná-la à sua coleção.",
|
||||||
"scrape-recipe-have-a-lot-of-recipes": "Tem muitas receitas a extrair de uma vez?",
|
"scrape-recipe-have-a-lot-of-recipes": "Tem muitas receitas a extrair de uma vez?",
|
||||||
"scrape-recipe-suggest-bulk-importer": "Tente o importador em massa",
|
"scrape-recipe-suggest-bulk-importer": "Tente o importador em massa",
|
||||||
"scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?",
|
"scrape-recipe-have-raw-html-or-json-data": "Tem dados HTML ou JSON brutos?",
|
||||||
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
||||||
"import-original-keywords-as-tags": "Importar palavras-chave originais como marcadores",
|
"import-original-keywords-as-tags": "Importar palavras-chave originais como marcadores",
|
||||||
"stay-in-edit-mode": "Permanecer no modo de edição",
|
"stay-in-edit-mode": "Permanecer no modo de edição",
|
||||||
@@ -662,22 +661,22 @@
|
|||||||
"no-food": "Sem Comida"
|
"no-food": "Sem Comida"
|
||||||
},
|
},
|
||||||
"reset-servings-count": "Reset Servings Count",
|
"reset-servings-count": "Reset Servings Count",
|
||||||
"not-linked-ingredients": "Additional Ingredients"
|
"not-linked-ingredients": "Ingredientes adicionais"
|
||||||
},
|
},
|
||||||
"recipe-finder": {
|
"recipe-finder": {
|
||||||
"recipe-finder": "Recipe Finder",
|
"recipe-finder": "Localizador de Receitas",
|
||||||
"recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.",
|
"recipe-finder-description": "Procure por receitas baseadas em ingredientes que você tem na mão. Você também pode filtrar por ferramentas disponíveis e definir um número máximo de ingredientes ou ferramentas que faltam.",
|
||||||
"selected-ingredients": "Selected Ingredients",
|
"selected-ingredients": "Ingredientes selecionados",
|
||||||
"no-ingredients-selected": "No ingredients selected",
|
"no-ingredients-selected": "Nenhum ingrediente selecionado",
|
||||||
"missing": "Missing",
|
"missing": "Ausente",
|
||||||
"no-recipes-found": "No recipes found",
|
"no-recipes-found": "Nenhuma receita encontrada",
|
||||||
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
||||||
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
||||||
"include-tools-on-hand": "Include Tools On Hand",
|
"include-tools-on-hand": "Include Tools On Hand",
|
||||||
"max-missing-ingredients": "Max Missing Ingredients",
|
"max-missing-ingredients": "Max Missing Ingredients",
|
||||||
"max-missing-tools": "Max Missing Tools",
|
"max-missing-tools": "Max Missing Tools",
|
||||||
"selected-tools": "Selected Tools",
|
"selected-tools": "Ferramentas Selecionadas",
|
||||||
"other-filters": "Other Filters",
|
"other-filters": "Outros Filtros",
|
||||||
"ready-to-make": "Ready to Make",
|
"ready-to-make": "Ready to Make",
|
||||||
"almost-ready-to-make": "Almost Ready to Make"
|
"almost-ready-to-make": "Almost Ready to Make"
|
||||||
},
|
},
|
||||||
@@ -690,7 +689,7 @@
|
|||||||
"or": "Ou",
|
"or": "Ou",
|
||||||
"has-any": "Tem alguma",
|
"has-any": "Tem alguma",
|
||||||
"has-all": "Tem todos",
|
"has-all": "Tem todos",
|
||||||
"clear-selection": "Clear Selection",
|
"clear-selection": "Limpar Seleção",
|
||||||
"results": "Resultados",
|
"results": "Resultados",
|
||||||
"search": "Pesquisar",
|
"search": "Pesquisar",
|
||||||
"search-mealie": "Pesquisar no Mealie (pressione /)",
|
"search-mealie": "Pesquisar no Mealie (pressione /)",
|
||||||
@@ -884,7 +883,7 @@
|
|||||||
"are-you-sure-you-want-to-check-all-items": "Tem certeza que deseja marcar todos os itens?",
|
"are-you-sure-you-want-to-check-all-items": "Tem certeza que deseja marcar todos os itens?",
|
||||||
"are-you-sure-you-want-to-uncheck-all-items": "Tem certeza que deseja desmarcar todos os itens?",
|
"are-you-sure-you-want-to-uncheck-all-items": "Tem certeza que deseja desmarcar todos os itens?",
|
||||||
"are-you-sure-you-want-to-delete-checked-items": "Tem certeza que deseja apagar todos os itens marcados?",
|
"are-you-sure-you-want-to-delete-checked-items": "Tem certeza que deseja apagar todos os itens marcados?",
|
||||||
"no-shopping-lists-found": "No Shopping Lists Found"
|
"no-shopping-lists-found": "Nenhuma lista de compras encontrada"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"all-recipes": "Todas as Receitas",
|
"all-recipes": "Todas as Receitas",
|
||||||
@@ -1286,17 +1285,17 @@
|
|||||||
"restore-from-v1-backup": "Tem uma cópia de segurança de uma instância anterior do Mealie v1? Você pode restaurá-la aqui.",
|
"restore-from-v1-backup": "Tem uma cópia de segurança de uma instância anterior do Mealie v1? Você pode restaurá-la aqui.",
|
||||||
"manage-profile-or-get-invite-link": "Gerencie seu próprio perfil, ou pegue um link de convite para compartilhar."
|
"manage-profile-or-get-invite-link": "Gerencie seu próprio perfil, ou pegue um link de convite para compartilhar."
|
||||||
},
|
},
|
||||||
"debug-openai-services": "Debug OpenAI Services",
|
"debug-openai-services": "Debug de Serviços OpenAI",
|
||||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
"debug-openai-services-description": "Use esta página para depurar serviços OpenAI. Você pode testar a sua conexão OpenAI e ver os resultados aqui. Se você tiver os serviços de imagem ativados, você também pode fornecer uma imagem.",
|
||||||
"run-test": "Run Test",
|
"run-test": "Executar teste",
|
||||||
"test-results": "Test Results",
|
"test-results": "Resultados do teste",
|
||||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||||
"household-delete-note": "Households with users cannot be deleted"
|
"household-delete-note": "Households with users cannot be deleted"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"welcome-user": "👋 Bem-vindo(a), {0}!",
|
"welcome-user": "👋 Bem-vindo(a), {0}!",
|
||||||
"description": "Gerencie seu perfil, receitas e configurações de grupo.",
|
"description": "Gerencie seu perfil, receitas e configurações de grupo.",
|
||||||
"invite-link": "Invite Link",
|
"invite-link": "Link de convite",
|
||||||
"get-invite-link": "Obter link de convite",
|
"get-invite-link": "Obter link de convite",
|
||||||
"get-public-link": "Obter link público",
|
"get-public-link": "Obter link público",
|
||||||
"account-summary": "Resumo da conta",
|
"account-summary": "Resumo da conta",
|
||||||
@@ -1367,21 +1366,21 @@
|
|||||||
"or": "OR"
|
"or": "OR"
|
||||||
},
|
},
|
||||||
"relational-operators": {
|
"relational-operators": {
|
||||||
"equals": "equals",
|
"equals": "igual a",
|
||||||
"does-not-equal": "does not equal",
|
"does-not-equal": "não é igual a",
|
||||||
"is-greater-than": "is greater than",
|
"is-greater-than": "é maior que",
|
||||||
"is-greater-than-or-equal-to": "is greater than or equal to",
|
"is-greater-than-or-equal-to": "é maior ou igual a",
|
||||||
"is-less-than": "is less than",
|
"is-less-than": "é menor que",
|
||||||
"is-less-than-or-equal-to": "is less than or equal to"
|
"is-less-than-or-equal-to": "é menor ou igual a"
|
||||||
},
|
},
|
||||||
"relational-keywords": {
|
"relational-keywords": {
|
||||||
"is": "is",
|
"is": "é",
|
||||||
"is-not": "is not",
|
"is-not": "não é",
|
||||||
"is-one-of": "is one of",
|
"is-one-of": "é um(a) de",
|
||||||
"is-not-one-of": "is not one of",
|
"is-not-one-of": "não é um de",
|
||||||
"contains-all-of": "contains all of",
|
"contains-all-of": "contém todos de",
|
||||||
"is-like": "is like",
|
"is-like": "é como",
|
||||||
"is-not-like": "is not like"
|
"is-not-like": "não é como"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Eu fiz isto",
|
"made-this": "Eu fiz isto",
|
||||||
"how-did-it-turn-out": "Que tal ficou?",
|
"how-did-it-turn-out": "Que tal ficou?",
|
||||||
"user-made-this": "{user} fez isto",
|
"user-made-this": "{user} fez isto",
|
||||||
"last-made-date": "Última vez {date}",
|
|
||||||
"api-extras-description": "Extras para receitas são funcionalidades chave da API Mealie. Estas permitem criar, dentro de uma receita, pares personalizados de chave/valor em JSON, para referência a partir de aplicações de terceiros. Pode usar essas chaves para fornecer informações, por exemplo, para acionar automações ou mensagens personalizadas para transmitir a um determinado dispositivo.",
|
"api-extras-description": "Extras para receitas são funcionalidades chave da API Mealie. Estas permitem criar, dentro de uma receita, pares personalizados de chave/valor em JSON, para referência a partir de aplicações de terceiros. Pode usar essas chaves para fornecer informações, por exemplo, para acionar automações ou mensagens personalizadas para transmitir a um determinado dispositivo.",
|
||||||
"message-key": "Chave de Mensagem",
|
"message-key": "Chave de Mensagem",
|
||||||
"parse": "Interpretar",
|
"parse": "Interpretar",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Am făcut asta",
|
"made-this": "Am făcut asta",
|
||||||
"how-did-it-turn-out": "Cum a ieșit?",
|
"how-did-it-turn-out": "Cum a ieșit?",
|
||||||
"user-made-this": "{user} a făcut asta",
|
"user-made-this": "{user} a făcut asta",
|
||||||
"last-made-date": "Ultima preparare {date}",
|
|
||||||
"api-extras-description": "Recipes extras sunt o caracteristică cheie a API-ului Mealie. Îți permit să creezi perechi personalizate de cheie/valoare JSON într-o rețetă, ca să faci referire la aplicații terțe. Puteți utiliza aceste chei pentru a furniza informații, de exemplu pentru a declanșa automatizări sau mesaje personalizate pentru a transmite dispozitivul dorit.",
|
"api-extras-description": "Recipes extras sunt o caracteristică cheie a API-ului Mealie. Îți permit să creezi perechi personalizate de cheie/valoare JSON într-o rețetă, ca să faci referire la aplicații terțe. Puteți utiliza aceste chei pentru a furniza informații, de exemplu pentru a declanșa automatizări sau mesaje personalizate pentru a transmite dispozitivul dorit.",
|
||||||
"message-key": "Cheie mesaj",
|
"message-key": "Cheie mesaj",
|
||||||
"parse": "Parsează",
|
"parse": "Parsează",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Я сделал это",
|
"made-this": "Я сделал это",
|
||||||
"how-did-it-turn-out": "Что получилось?",
|
"how-did-it-turn-out": "Что получилось?",
|
||||||
"user-made-this": "{user} сделал это",
|
"user-made-this": "{user} сделал это",
|
||||||
"last-made-date": "Последний раз сделано {date}",
|
|
||||||
"api-extras-description": "Дополнения к рецептам являются ключевым элементом Mealie API. Они позволяют создавать пользовательские пары json ключ/значение в рецепте для ссылания на другие приложения. Вы можете использовать эти ключи, чтобы сохранить нужную информацию, например, для автоматизаций или уведомлений на ваши устройства.",
|
"api-extras-description": "Дополнения к рецептам являются ключевым элементом Mealie API. Они позволяют создавать пользовательские пары json ключ/значение в рецепте для ссылания на другие приложения. Вы можете использовать эти ключи, чтобы сохранить нужную информацию, например, для автоматизаций или уведомлений на ваши устройства.",
|
||||||
"message-key": "Ключ сообщения",
|
"message-key": "Ключ сообщения",
|
||||||
"parse": "Обработать",
|
"parse": "Обработать",
|
||||||
@@ -601,7 +600,7 @@
|
|||||||
"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-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.",
|
"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-image": "Create from Image",
|
"create-from-image": "Create from Image",
|
||||||
"should-translate-description": "Translate the recipe into my language",
|
"should-translate-description": "Перевести рецепт на мой язык",
|
||||||
"please-wait-image-procesing": "Подождите, идет обработка изображения.",
|
"please-wait-image-procesing": "Подождите, идет обработка изображения.",
|
||||||
"bulk-url-import": "Массовый импорт по URL",
|
"bulk-url-import": "Массовый импорт по URL",
|
||||||
"debug-scraper": "Отладка сканирования",
|
"debug-scraper": "Отладка сканирования",
|
||||||
@@ -611,16 +610,16 @@
|
|||||||
"scrape-recipe-description": "Отсканировать рецепт по ссылке. Предоставьте ссылку на страницу, которую вы хотите отсканировать, и Mealie попытается вырезать рецепт с этого сайта и добавить его в свою коллекцию.",
|
"scrape-recipe-description": "Отсканировать рецепт по ссылке. Предоставьте ссылку на страницу, которую вы хотите отсканировать, и Mealie попытается вырезать рецепт с этого сайта и добавить его в свою коллекцию.",
|
||||||
"scrape-recipe-have-a-lot-of-recipes": "Хотите отсканировать несколько рецептов за раз?",
|
"scrape-recipe-have-a-lot-of-recipes": "Хотите отсканировать несколько рецептов за раз?",
|
||||||
"scrape-recipe-suggest-bulk-importer": "Воспользуйтесь массовым импортом",
|
"scrape-recipe-suggest-bulk-importer": "Воспользуйтесь массовым импортом",
|
||||||
"scrape-recipe-have-raw-html-or-json-data": "Have raw HTML or JSON data?",
|
"scrape-recipe-have-raw-html-or-json-data": "У Вас есть данные HTML или JSON?",
|
||||||
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
"scrape-recipe-you-can-import-from-raw-data-directly": "You can import from raw data directly",
|
||||||
"import-original-keywords-as-tags": "Импортировать исходные ключевые слова как теги",
|
"import-original-keywords-as-tags": "Импортировать исходные ключевые слова как теги",
|
||||||
"stay-in-edit-mode": "Остаться в режиме редактирования",
|
"stay-in-edit-mode": "Остаться в режиме редактирования",
|
||||||
"import-from-zip": "Импорт из архива",
|
"import-from-zip": "Импорт из архива",
|
||||||
"import-from-zip-description": "Импорт одного рецепта, который был экспортирован из другого экземпляра Mealie.",
|
"import-from-zip-description": "Импорт одного рецепта, который был экспортирован из другого экземпляра Mealie.",
|
||||||
"import-from-html-or-json": "Import from HTML or JSON",
|
"import-from-html-or-json": "Импортировать из HTML или JSON",
|
||||||
"import-from-html-or-json-description": "Import a single recipe from raw HTML or JSON. This is useful if you have a recipe from a site that Mealie can't scrape normally, or from some other external source.",
|
"import-from-html-or-json-description": "Import a single recipe from raw HTML or JSON. This is useful if you have a recipe from a site that Mealie can't scrape normally, or from some other external source.",
|
||||||
"json-import-format-description-colon": "To import via JSON, it must be in valid format:",
|
"json-import-format-description-colon": "To import via JSON, it must be in valid format:",
|
||||||
"json-editor": "JSON Editor",
|
"json-editor": "Редактор JSON",
|
||||||
"zip-files-must-have-been-exported-from-mealie": ".zip файлы должны быть экспортированы из Mealie",
|
"zip-files-must-have-been-exported-from-mealie": ".zip файлы должны быть экспортированы из Mealie",
|
||||||
"create-a-recipe-by-uploading-a-scan": "Создайте рецепт, загрузив скан.",
|
"create-a-recipe-by-uploading-a-scan": "Создайте рецепт, загрузив скан.",
|
||||||
"upload-a-png-image-from-a-recipe-book": "Загрузить png изображение из книги рецептов",
|
"upload-a-png-image-from-a-recipe-book": "Загрузить png изображение из книги рецептов",
|
||||||
@@ -661,16 +660,16 @@
|
|||||||
"missing-food": "Create missing food: {food}",
|
"missing-food": "Create missing food: {food}",
|
||||||
"no-food": "Нет еды"
|
"no-food": "Нет еды"
|
||||||
},
|
},
|
||||||
"reset-servings-count": "Reset Servings Count",
|
"reset-servings-count": "Сбросить количество порций",
|
||||||
"not-linked-ingredients": "Additional Ingredients"
|
"not-linked-ingredients": "Дополнительные ингредиенты"
|
||||||
},
|
},
|
||||||
"recipe-finder": {
|
"recipe-finder": {
|
||||||
"recipe-finder": "Recipe Finder",
|
"recipe-finder": "Поиск рецептов",
|
||||||
"recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.",
|
"recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.",
|
||||||
"selected-ingredients": "Selected Ingredients",
|
"selected-ingredients": "Выбранные ингредиенты",
|
||||||
"no-ingredients-selected": "No ingredients selected",
|
"no-ingredients-selected": "No ingredients selected",
|
||||||
"missing": "Missing",
|
"missing": "Missing",
|
||||||
"no-recipes-found": "No recipes found",
|
"no-recipes-found": "Рецепты не найдены",
|
||||||
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
||||||
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
||||||
"include-tools-on-hand": "Include Tools On Hand",
|
"include-tools-on-hand": "Include Tools On Hand",
|
||||||
@@ -881,10 +880,10 @@
|
|||||||
"completed-on": "Выполнено в {date}",
|
"completed-on": "Выполнено в {date}",
|
||||||
"you-are-offline": "Вы не в сети",
|
"you-are-offline": "Вы не в сети",
|
||||||
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
|
"you-are-offline-description": "Not all features are available while offline. You can still add, modify, and remove items, but you will not be able to sync your changes to the server until you are back online.",
|
||||||
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
|
"are-you-sure-you-want-to-check-all-items": "Вы уверены, что хотите выбрать все элементы?",
|
||||||
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
|
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
|
||||||
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
|
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
|
||||||
"no-shopping-lists-found": "No Shopping Lists Found"
|
"no-shopping-lists-found": "Списки покупок не найдены"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"all-recipes": "Все рецепты",
|
"all-recipes": "Все рецепты",
|
||||||
@@ -1286,17 +1285,17 @@
|
|||||||
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
|
"restore-from-v1-backup": "Have a backup from a previous instance of Mealie v1? You can restore it here.",
|
||||||
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."
|
"manage-profile-or-get-invite-link": "Manage your own profile, or grab an invite link to share with others."
|
||||||
},
|
},
|
||||||
"debug-openai-services": "Debug OpenAI Services",
|
"debug-openai-services": "Отладка OpenAI сервисов",
|
||||||
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
"debug-openai-services-description": "Use this page to debug OpenAI services. You can test your OpenAI connection and see the results here. If you have image services enabled, you can also provide an image.",
|
||||||
"run-test": "Run Test",
|
"run-test": "Запустить тест",
|
||||||
"test-results": "Test Results",
|
"test-results": "Результаты тестов",
|
||||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
"group-delete-note": "Groups with users or households cannot be deleted",
|
||||||
"household-delete-note": "Households with users cannot be deleted"
|
"household-delete-note": "Households with users cannot be deleted"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"welcome-user": "👋 Добро пожаловать, {0}!",
|
"welcome-user": "👋 Добро пожаловать, {0}!",
|
||||||
"description": "Управление настройками профиля, рецептов и группы.",
|
"description": "Управление настройками профиля, рецептов и группы.",
|
||||||
"invite-link": "Invite Link",
|
"invite-link": "Ссылка-приглашение",
|
||||||
"get-invite-link": "Получить ссылку для приглашения",
|
"get-invite-link": "Получить ссылку для приглашения",
|
||||||
"get-public-link": "Получить публичную ссылку",
|
"get-public-link": "Получить публичную ссылку",
|
||||||
"account-summary": "Cведения об учетной записи",
|
"account-summary": "Cведения об учетной записи",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Toto som uvaril",
|
"made-this": "Toto som uvaril",
|
||||||
"how-did-it-turn-out": "Ako to dopadlo?",
|
"how-did-it-turn-out": "Ako to dopadlo?",
|
||||||
"user-made-this": "{user} toto uvaril/-a",
|
"user-made-this": "{user} toto uvaril/-a",
|
||||||
"last-made-date": "Posledne pripravené {date}",
|
|
||||||
"api-extras-description": "API dolnky receptov sú kľúčovou funkcionalitou Mealie API. Umožňujú užívateľom vytvárať vlastné JSON páry kľúč/hodnota v rámci receptu, a využiť v aplikáciách tretích strán. Údaje uložené pod jednotlivými kľúčmi je možné využiť napríklad ako spúšťač automatizovaných procesov, či pri zasielaní vlastných správ do vami zvolených zariadení.",
|
"api-extras-description": "API dolnky receptov sú kľúčovou funkcionalitou Mealie API. Umožňujú užívateľom vytvárať vlastné JSON páry kľúč/hodnota v rámci receptu, a využiť v aplikáciách tretích strán. Údaje uložené pod jednotlivými kľúčmi je možné využiť napríklad ako spúšťač automatizovaných procesov, či pri zasielaní vlastných správ do vami zvolených zariadení.",
|
||||||
"message-key": "Kľúč správy",
|
"message-key": "Kľúč správy",
|
||||||
"parse": "Analyzovať",
|
"parse": "Analyzovať",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Naredil sem to",
|
"made-this": "Naredil sem to",
|
||||||
"how-did-it-turn-out": "Kako se je izkazalo?",
|
"how-did-it-turn-out": "Kako se je izkazalo?",
|
||||||
"user-made-this": "{user} je tole pripravil/a",
|
"user-made-this": "{user} je tole pripravil/a",
|
||||||
"last-made-date": "Nazadnje pripravljen {date}",
|
|
||||||
"api-extras-description": "Dodatni podatki za recepte so ključna funkcionalnost Mealie APIja. Omogočajo ustvarjanje lastnih JSON ključ / vrednost parov v okviru recepta, da lahko do njih dostopajo zunanje aplikacije. Te ključe lahko uporabiš za posredovanje informacij, na primer za sprožanje avtomatike ali sporočanje prilagojenih sporočil na poljubno napravo.",
|
"api-extras-description": "Dodatni podatki za recepte so ključna funkcionalnost Mealie APIja. Omogočajo ustvarjanje lastnih JSON ključ / vrednost parov v okviru recepta, da lahko do njih dostopajo zunanje aplikacije. Te ključe lahko uporabiš za posredovanje informacij, na primer za sprožanje avtomatike ali sporočanje prilagojenih sporočil na poljubno napravo.",
|
||||||
"message-key": "Ključ sporočila",
|
"message-key": "Ključ sporočila",
|
||||||
"parse": "Razloči",
|
"parse": "Razloči",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Последњи пут прављено {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
"category-filter": "Kategorifilter",
|
"category-filter": "Kategorifilter",
|
||||||
"category-update-failed": "Kategori gick inte att uppdatera",
|
"category-update-failed": "Kategori gick inte att uppdatera",
|
||||||
"category-updated": "Kategori uppdaterad",
|
"category-updated": "Kategori uppdaterad",
|
||||||
"uncategorized-count": "Ingen Kategori {count}",
|
"uncategorized-count": "Utan kategori {count}",
|
||||||
"create-a-category": "Skapa kategori",
|
"create-a-category": "Skapa kategori",
|
||||||
"category-name": "Kategorinamn",
|
"category-name": "Kategorinamn",
|
||||||
"category": "Kategori"
|
"category": "Kategori"
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Jag lagade den här",
|
"made-this": "Jag lagade den här",
|
||||||
"how-did-it-turn-out": "Hur blev rätten?",
|
"how-did-it-turn-out": "Hur blev rätten?",
|
||||||
"user-made-this": "{user} lagade detta",
|
"user-made-this": "{user} lagade detta",
|
||||||
"last-made-date": "Senast lagad {date}",
|
|
||||||
"api-extras-description": "Recept API-tillägg är en viktig funktion i Mealie's API. Med hjälp av dem kan du skapa anpassade JSON-nyckel/värdepar i ett recept, som du kan referera till från tredjepartsapplikationer. Du kan använda dessa nycklar för att tillhandahålla information, till exempel för att trigga automatiseringar eller anpassade meddelanden som ska vidarebefordras till önskad enhet.",
|
"api-extras-description": "Recept API-tillägg är en viktig funktion i Mealie's API. Med hjälp av dem kan du skapa anpassade JSON-nyckel/värdepar i ett recept, som du kan referera till från tredjepartsapplikationer. Du kan använda dessa nycklar för att tillhandahålla information, till exempel för att trigga automatiseringar eller anpassade meddelanden som ska vidarebefordras till önskad enhet.",
|
||||||
"message-key": "Meddelandenyckel",
|
"message-key": "Meddelandenyckel",
|
||||||
"parse": "Läs in",
|
"parse": "Läs in",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Bunu ben yaptım",
|
"made-this": "Bunu ben yaptım",
|
||||||
"how-did-it-turn-out": "Nasıl oldu?",
|
"how-did-it-turn-out": "Nasıl oldu?",
|
||||||
"user-made-this": "{user} bunu yaptı",
|
"user-made-this": "{user} bunu yaptı",
|
||||||
"last-made-date": "En Son {date} Yapıldı",
|
|
||||||
"api-extras-description": "Tarif ekstraları Mealie API'nin önemli bir özelliğidir. Üçüncü taraf uygulamalardan referans almak üzere bir tarif içinde özel JSON anahtar/değer çiftleri oluşturmanıza olanak tanır. Bu tuşları, örneğin otomasyonları tetiklemek veya istediğiniz cihaza iletilecek özel mesajları bilgi sağlamak için kullanabilirsiniz.",
|
"api-extras-description": "Tarif ekstraları Mealie API'nin önemli bir özelliğidir. Üçüncü taraf uygulamalardan referans almak üzere bir tarif içinde özel JSON anahtar/değer çiftleri oluşturmanıza olanak tanır. Bu tuşları, örneğin otomasyonları tetiklemek veya istediğiniz cihaza iletilecek özel mesajları bilgi sağlamak için kullanabilirsiniz.",
|
||||||
"message-key": "İleti Anahtarı",
|
"message-key": "İleti Anahtarı",
|
||||||
"parse": "Ayrıştırma",
|
"parse": "Ayrıştırma",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"api-docs": "Документація API",
|
"api-docs": "Документація API",
|
||||||
"api-port": "Порт API",
|
"api-port": "Порт API",
|
||||||
"application-mode": "Режим додатку",
|
"application-mode": "Режим додатку",
|
||||||
"database-type": "Тип бази данних",
|
"database-type": "Тип бази даних",
|
||||||
"database-url": "URL-адреса бази даних",
|
"database-url": "URL-адреса бази даних",
|
||||||
"default-group": "Групи за замовчуванням",
|
"default-group": "Групи за замовчуванням",
|
||||||
"default-household": "Сімʼя за замовчуванням",
|
"default-household": "Сімʼя за замовчуванням",
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
"a-name-is-required": "Необхідно вказати назву",
|
"a-name-is-required": "Необхідно вказати назву",
|
||||||
"delete-with-name": "Видалити {name}",
|
"delete-with-name": "Видалити {name}",
|
||||||
"confirm-delete-generic-with-name": "Ви дійсно хочете видалити {name}?",
|
"confirm-delete-generic-with-name": "Ви дійсно хочете видалити {name}?",
|
||||||
"confirm-delete-own-admin-account": "Зверніть увагу, що ви намагаєтеся видалити свій обліковий запис адміністратора! Цю дію неможливо скасувати і ви остаточно видалите ваш обліковий запис?",
|
"confirm-delete-own-admin-account": "Зверніть увагу, що ви намагаєтеся видалити свій обліковий запис адміністратора! Цю дію неможливо скасувати й ви остаточно видалите ваш обліковий запис?",
|
||||||
"organizer": "Організатор",
|
"organizer": "Організатор",
|
||||||
"transfer": "Передача",
|
"transfer": "Передача",
|
||||||
"copy": "Скопіювати",
|
"copy": "Скопіювати",
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"learn-more": "Дізнатися більше",
|
"learn-more": "Дізнатися більше",
|
||||||
"this-feature-is-currently-inactive": "Ця функція наразі не активна",
|
"this-feature-is-currently-inactive": "Ця функція наразі не активна",
|
||||||
"clipboard-not-supported": "Буфер обміну не підтримується",
|
"clipboard-not-supported": "Буфер обміну не підтримується",
|
||||||
"copied-to-clipboard": "Скопійовано до буферу обміну",
|
"copied-to-clipboard": "Скопійовано до буфера обміну",
|
||||||
"your-browser-does-not-support-clipboard": "Ваш браузер не підтримує буфер обміну",
|
"your-browser-does-not-support-clipboard": "Ваш браузер не підтримує буфер обміну",
|
||||||
"copied-items-to-clipboard": "Жоден елемент не скопійовано в буфер обміну|Один елемент скопійовано в буфер обміну|Скопійовано {count} елементів в буфер обміну",
|
"copied-items-to-clipboard": "Жоден елемент не скопійовано в буфер обміну|Один елемент скопійовано в буфер обміну|Скопійовано {count} елементів в буфер обміну",
|
||||||
"actions": "Дії",
|
"actions": "Дії",
|
||||||
@@ -246,14 +246,14 @@
|
|||||||
"manage-members": "Керування Користувачами",
|
"manage-members": "Керування Користувачами",
|
||||||
"manage-members-description": "Керуйте дозволами учасників вашої сімʼї. {manage} дозволяє користувачеві отримати доступ до сторінки керування даними {invite} дозволяє користувачеві генерувати посилання запрошення для інших користувачів. Власники групи не можуть змінити власні дозволи.",
|
"manage-members-description": "Керуйте дозволами учасників вашої сімʼї. {manage} дозволяє користувачеві отримати доступ до сторінки керування даними {invite} дозволяє користувачеві генерувати посилання запрошення для інших користувачів. Власники групи не можуть змінити власні дозволи.",
|
||||||
"manage": "Керування",
|
"manage": "Керування",
|
||||||
"manage-household": "Manage Household",
|
"manage-household": "Керувати сімʼєю",
|
||||||
"invite": "Запрошення",
|
"invite": "Запрошення",
|
||||||
"looking-to-update-your-profile": "Бажаєте оновити свій профіль?",
|
"looking-to-update-your-profile": "Бажаєте оновити свій профіль?",
|
||||||
"default-recipe-preferences-description": "Це типові налаштування, коли створюється новий рецепт у вашій групі. Ці параметри можна змінити для окремих рецептів в меню налаштувань рецептів.",
|
"default-recipe-preferences-description": "Це типові налаштування, коли створюється новий рецепт у вашій групі. Ці параметри можна змінити для окремих рецептів в меню налаштувань рецептів.",
|
||||||
"default-recipe-preferences": "Параметри за умовчанням",
|
"default-recipe-preferences": "Параметри за умовчанням",
|
||||||
"group-preferences": "Налаштування групи",
|
"group-preferences": "Налаштування групи",
|
||||||
"private-group": "Приватна група",
|
"private-group": "Приватна група",
|
||||||
"private-group-description": "Setting your group to private will disable all public view options. This overrides any individual public view settings",
|
"private-group-description": "Якщо зробити групу приватною, то всі налаштування публічного перегляду буде скинуто. Це замінить індивідуальні налаштування публічного перегляду",
|
||||||
"enable-public-access": "Дозволити загальний доступ",
|
"enable-public-access": "Дозволити загальний доступ",
|
||||||
"enable-public-access-description": "Робить групові рецепти загальнодоступними за замовчуванням і дозволяє користувачам переглядати рецепти без входу в систему",
|
"enable-public-access-description": "Робить групові рецепти загальнодоступними за замовчуванням і дозволяє користувачам переглядати рецепти без входу в систему",
|
||||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Дозволити користувачам за межами вашої групи бачити ваші рецепти",
|
"allow-users-outside-of-your-group-to-see-your-recipes": "Дозволити користувачам за межами вашої групи бачити ваші рецепти",
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
"admin-group-management-text": "Зміни до цієї групи будуть відображені негайно.",
|
"admin-group-management-text": "Зміни до цієї групи будуть відображені негайно.",
|
||||||
"group-id-value": "Id групи: {0}",
|
"group-id-value": "Id групи: {0}",
|
||||||
"total-households": "Всього сімей",
|
"total-households": "Всього сімей",
|
||||||
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
|
"you-must-select-a-group-before-selecting-a-household": "Ви маєте вибрати групу перед тим, як вибирати сім'ю"
|
||||||
},
|
},
|
||||||
"household": {
|
"household": {
|
||||||
"household": "Сімʼя",
|
"household": "Сімʼя",
|
||||||
@@ -292,9 +292,9 @@
|
|||||||
"admin-household-management-text": "Зміни до цієї сімʼї будуть відображені негайно.",
|
"admin-household-management-text": "Зміни до цієї сімʼї будуть відображені негайно.",
|
||||||
"household-id-value": "Ідентифікатор сімʼї: {0}",
|
"household-id-value": "Ідентифікатор сімʼї: {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",
|
"private-household-description": "Якщо зробити сім'ю приватною, то всі налаштування публічного перегляду буде скинуто. Це замінить індивідуальні налаштування публічного перегляду",
|
||||||
"lock-recipe-edits-from-other-households": "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",
|
"lock-recipe-edits-from-other-households-description": "Якщо увімкнено, тільки члени вашої сім'ї зможуть редагувати рецепти, які були створені вашою сім'єю",
|
||||||
"household-recipe-preferences": "Налаштування рецептів сімʼї",
|
"household-recipe-preferences": "Налаштування рецептів сімʼї",
|
||||||
"default-recipe-preferences-description": "Це типові налаштування для нового рецепта у вашій сімʼї. Ці параметри можна змінити для окремих рецептів в меню налаштувань рецептів.",
|
"default-recipe-preferences-description": "Це типові налаштування для нового рецепта у вашій сімʼї. Ці параметри можна змінити для окремих рецептів в меню налаштувань рецептів.",
|
||||||
"allow-users-outside-of-your-household-to-see-your-recipes": "Дозволити користувачам за межами вашої сімʼї бачити ваші рецепти",
|
"allow-users-outside-of-your-household-to-see-your-recipes": "Дозволити користувачам за межами вашої сімʼї бачити ваші рецепти",
|
||||||
@@ -321,10 +321,10 @@
|
|||||||
"mealplan-settings": "Налаштування плану харчування",
|
"mealplan-settings": "Налаштування плану харчування",
|
||||||
"mealplan-update-failed": "Не вдалося оновити план харчування",
|
"mealplan-update-failed": "Не вдалося оновити план харчування",
|
||||||
"mealplan-updated": "План харчування оновлено",
|
"mealplan-updated": "План харчування оновлено",
|
||||||
"mealplan-households-description": "If no household is selected, recipes can be added from any household",
|
"mealplan-households-description": "Якщо жодної сім'ї не вибрано, рецепти можуть бути доданими з будь-якої сім'ї",
|
||||||
"any-category": "Будь-яка категорія",
|
"any-category": "Будь-яка категорія",
|
||||||
"any-tag": "Будь-який тег",
|
"any-tag": "Будь-який тег",
|
||||||
"any-household": "Any Household",
|
"any-household": "Будь-яка сім'я",
|
||||||
"no-meal-plan-defined-yet": "Не створено жодного плану харчування",
|
"no-meal-plan-defined-yet": "Не створено жодного плану харчування",
|
||||||
"no-meal-planned-for-today": "Не заплановано харчування на сьогодні",
|
"no-meal-planned-for-today": "Не заплановано харчування на сьогодні",
|
||||||
"numberOfDays-hint": "Скільки днів завантажувати на сторінку",
|
"numberOfDays-hint": "Скільки днів завантажувати на сторінку",
|
||||||
@@ -357,7 +357,7 @@
|
|||||||
"for-type-meal-types": "для {0} типів харчування",
|
"for-type-meal-types": "для {0} типів харчування",
|
||||||
"meal-plan-rules": "Правила планів харчування",
|
"meal-plan-rules": "Правила планів харчування",
|
||||||
"new-rule": "Нове правило",
|
"new-rule": "Нове правило",
|
||||||
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the rule filters will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
|
"meal-plan-rules-description": "Ви можете створити правила для автоматичного вибору рецептів для ваших планів харчування. Ці правила використовуються сервером для вибору рецептів при створенні плану харчування. Зверніть увагу, що якщо правила мають обмеження на день/тип, то їх категорії будуть об'єднані. Дублювати правила немає сенсу, але можливо.",
|
||||||
"new-rule-description": "При створенні нового правила для плану харчування, ви можете обмежити правило на певний день тижня та/або певний тип їжі. Щоб застосувати правило до всіх днів або всіх типів їжі, ви можете встановити правило \"Будь-який\", що застосовуватиме його до всіх можливих значень для дня та/або типу їжі.",
|
"new-rule-description": "При створенні нового правила для плану харчування, ви можете обмежити правило на певний день тижня та/або певний тип їжі. Щоб застосувати правило до всіх днів або всіх типів їжі, ви можете встановити правило \"Будь-який\", що застосовуватиме його до всіх можливих значень для дня та/або типу їжі.",
|
||||||
"recipe-rules": "Правила рецептів",
|
"recipe-rules": "Правила рецептів",
|
||||||
"applies-to-all-days": "Застосовується до всіх днів",
|
"applies-to-all-days": "Застосовується до всіх днів",
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
"save-recipe-before-use": "Зберегти рецепт перед використанням",
|
"save-recipe-before-use": "Зберегти рецепт перед використанням",
|
||||||
"section-title": "Назва розділу",
|
"section-title": "Назва розділу",
|
||||||
"servings": "Порції",
|
"servings": "Порції",
|
||||||
"serves-amount": "Serves {amount}",
|
"serves-amount": "Порцій: {amount}",
|
||||||
"share-recipe-message": "Я хотів би поділитися з тобою своїм рецептом {0}.",
|
"share-recipe-message": "Я хотів би поділитися з тобою своїм рецептом {0}.",
|
||||||
"show-nutrition-values": "Показати харчову цінність",
|
"show-nutrition-values": "Показати харчову цінність",
|
||||||
"sodium-content": "Натрій",
|
"sodium-content": "Натрій",
|
||||||
@@ -547,8 +547,8 @@
|
|||||||
"failed-to-add-recipe-to-mealplan": "Не вдалося додати рецепт до плану харчування",
|
"failed-to-add-recipe-to-mealplan": "Не вдалося додати рецепт до плану харчування",
|
||||||
"failed-to-add-to-list": "Не вдалося додати до списку",
|
"failed-to-add-to-list": "Не вдалося додати до списку",
|
||||||
"yield": "Вихід",
|
"yield": "Вихід",
|
||||||
"yields-amount-with-text": "Yields {amount} {text}",
|
"yields-amount-with-text": "Вийде: {amount} {text}",
|
||||||
"yield-text": "Yield Text",
|
"yield-text": "Текст виходу",
|
||||||
"quantity": "Кількість",
|
"quantity": "Кількість",
|
||||||
"choose-unit": "Виберіть одиниці вимірювання",
|
"choose-unit": "Виберіть одиниці вимірювання",
|
||||||
"press-enter-to-create": "Натисніть Enter, щоб створити",
|
"press-enter-to-create": "Натисніть Enter, щоб створити",
|
||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "Я це приготував",
|
"made-this": "Я це приготував",
|
||||||
"how-did-it-turn-out": "Як вийшло?",
|
"how-did-it-turn-out": "Як вийшло?",
|
||||||
"user-made-this": "{user} зробив це",
|
"user-made-this": "{user} зробив це",
|
||||||
"last-made-date": "Востаннє приготовано {date}",
|
|
||||||
"api-extras-description": "Додатки в рецептах - ключова функція API Mealie. Вони дозволяють створювати користувацьку пару JSON ключів та значень в рецепті для сторонніх додатків. Це можна використовувати для автоматизації або для створення користувацьких повідомлень для сторонніх сервісів.",
|
"api-extras-description": "Додатки в рецептах - ключова функція API Mealie. Вони дозволяють створювати користувацьку пару JSON ключів та значень в рецепті для сторонніх додатків. Це можна використовувати для автоматизації або для створення користувацьких повідомлень для сторонніх сервісів.",
|
||||||
"message-key": "Ключ повідомлення",
|
"message-key": "Ключ повідомлення",
|
||||||
"parse": "Проаналізувати",
|
"parse": "Проаналізувати",
|
||||||
@@ -662,24 +661,24 @@
|
|||||||
"no-food": "Немає їжі"
|
"no-food": "Немає їжі"
|
||||||
},
|
},
|
||||||
"reset-servings-count": "Скинути кількість порцій",
|
"reset-servings-count": "Скинути кількість порцій",
|
||||||
"not-linked-ingredients": "Additional Ingredients"
|
"not-linked-ingredients": "Додаткові продукти"
|
||||||
},
|
},
|
||||||
"recipe-finder": {
|
"recipe-finder": {
|
||||||
"recipe-finder": "Recipe Finder",
|
"recipe-finder": "Шукач рецептів",
|
||||||
"recipe-finder-description": "Search for recipes based on ingredients you have on hand. You can also filter by tools you have available, and set a maximum number of missing ingredients or tools.",
|
"recipe-finder-description": "Пошук рецептів базується на продуктах, які ви маєте. Ви також можете фільтрувати за наявними інструментами та встановити максимальну кількість відсутніх продуктів або інструментів.",
|
||||||
"selected-ingredients": "Selected Ingredients",
|
"selected-ingredients": "Вибрані продукти",
|
||||||
"no-ingredients-selected": "No ingredients selected",
|
"no-ingredients-selected": "Жодного продукту не вибрано",
|
||||||
"missing": "Missing",
|
"missing": "Відсутні",
|
||||||
"no-recipes-found": "No recipes found",
|
"no-recipes-found": "Рецептів не знайдено",
|
||||||
"no-recipes-found-description": "Try adding more ingredients to your search or adjusting your filters",
|
"no-recipes-found-description": "Спробуйте додати більше продуктів до пошукового списку або підлаштувати фільтри",
|
||||||
"include-ingredients-on-hand": "Include Ingredients On Hand",
|
"include-ingredients-on-hand": "Включити наявні продукти",
|
||||||
"include-tools-on-hand": "Include Tools On Hand",
|
"include-tools-on-hand": "Включити наявні інструменти",
|
||||||
"max-missing-ingredients": "Max Missing Ingredients",
|
"max-missing-ingredients": "Максимум відсутніх продуктів",
|
||||||
"max-missing-tools": "Max Missing Tools",
|
"max-missing-tools": "Максимум відсутніх інструментів",
|
||||||
"selected-tools": "Selected Tools",
|
"selected-tools": "Вибрані інструменти",
|
||||||
"other-filters": "Other Filters",
|
"other-filters": "Інші фільтри",
|
||||||
"ready-to-make": "Ready to Make",
|
"ready-to-make": "Готове до приготування",
|
||||||
"almost-ready-to-make": "Almost Ready to Make"
|
"almost-ready-to-make": "Майже готове до приготування"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"advanced-search": "Розширений пошук",
|
"advanced-search": "Розширений пошук",
|
||||||
@@ -884,7 +883,7 @@
|
|||||||
"are-you-sure-you-want-to-check-all-items": "Ви впевнені, що хочете відмітити всі елементи?",
|
"are-you-sure-you-want-to-check-all-items": "Ви впевнені, що хочете відмітити всі елементи?",
|
||||||
"are-you-sure-you-want-to-uncheck-all-items": "Ви впевнені, що хочете зняти відмітку з усіх елементів?",
|
"are-you-sure-you-want-to-uncheck-all-items": "Ви впевнені, що хочете зняти відмітку з усіх елементів?",
|
||||||
"are-you-sure-you-want-to-delete-checked-items": "Ви впевнені, що хочете видалити всі відмічені елементи?",
|
"are-you-sure-you-want-to-delete-checked-items": "Ви впевнені, що хочете видалити всі відмічені елементи?",
|
||||||
"no-shopping-lists-found": "No Shopping Lists Found"
|
"no-shopping-lists-found": "Списків покупок не знайдено"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"all-recipes": "Всі рецепти",
|
"all-recipes": "Всі рецепти",
|
||||||
@@ -1030,7 +1029,7 @@
|
|||||||
"administrator": "Адміністратор",
|
"administrator": "Адміністратор",
|
||||||
"user-can-invite-other-to-group": "Користувач може запрошувати інших в групу",
|
"user-can-invite-other-to-group": "Користувач може запрошувати інших в групу",
|
||||||
"user-can-manage-group": "Користувач може керувати групою",
|
"user-can-manage-group": "Користувач може керувати групою",
|
||||||
"user-can-manage-household": "User can manage household",
|
"user-can-manage-household": "Користувач може управляти сім'єю",
|
||||||
"user-can-organize-group-data": "Користувач може впорядковувати дані групи",
|
"user-can-organize-group-data": "Користувач може впорядковувати дані групи",
|
||||||
"enable-advanced-features": "Увімкнути додаткові функції",
|
"enable-advanced-features": "Увімкнути додаткові функції",
|
||||||
"it-looks-like-this-is-your-first-time-logging-in": "Схоже, ви заходите вперше.",
|
"it-looks-like-this-is-your-first-time-logging-in": "Схоже, ви заходите вперше.",
|
||||||
@@ -1290,13 +1289,13 @@
|
|||||||
"debug-openai-services-description": "Використовуйте цю сторінку, щоб налагодити служби OpenAI. Ви можете перевірити ваше з'єднання з OpenAI й побачити результати тут. Якщо ввімкнено служби зображень, ви також можете надати зображення.",
|
"debug-openai-services-description": "Використовуйте цю сторінку, щоб налагодити служби OpenAI. Ви можете перевірити ваше з'єднання з OpenAI й побачити результати тут. Якщо ввімкнено служби зображень, ви також можете надати зображення.",
|
||||||
"run-test": "Запустити перевірку",
|
"run-test": "Запустити перевірку",
|
||||||
"test-results": "Результати перевірки",
|
"test-results": "Результати перевірки",
|
||||||
"group-delete-note": "Groups with users or households cannot be deleted",
|
"group-delete-note": "Не можна видалити групи з користувачами чи сім'ями в ній",
|
||||||
"household-delete-note": "Households with users cannot be deleted"
|
"household-delete-note": "Не можна видалити сім'ю з користувачами в ній"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"welcome-user": "👋 Ласкаво просимо, {0}!",
|
"welcome-user": "👋 Ласкаво просимо, {0}!",
|
||||||
"description": "Керування вашим профілем, рецептами та налаштуваннями групи.",
|
"description": "Керування вашим профілем, рецептами та налаштуваннями групи.",
|
||||||
"invite-link": "Invite Link",
|
"invite-link": "Посилання-запрошення",
|
||||||
"get-invite-link": "Отримати посилання-запрошення",
|
"get-invite-link": "Отримати посилання-запрошення",
|
||||||
"get-public-link": "Отримати публічне посилання",
|
"get-public-link": "Отримати публічне посилання",
|
||||||
"account-summary": "Аккаунт",
|
"account-summary": "Аккаунт",
|
||||||
@@ -1345,9 +1344,9 @@
|
|||||||
},
|
},
|
||||||
"cookbook": {
|
"cookbook": {
|
||||||
"cookbooks": "Кулінарні книги",
|
"cookbooks": "Кулінарні книги",
|
||||||
"description": "Cookbooks are another way to organize recipes by creating cross sections of recipes, organizers, and other filters. Creating a cookbook will add an entry to the side-bar and all the recipes with the filters chosen will be displayed in the cookbook.",
|
"description": "Кулінарні книги - це ще один спосіб організовувати рецепти за допомогою розділів та інших фільтрів. Нова кулінарна книга з'явиться на боковій панелі, і всі рецепти, які відповідають обраним фільтрам, будуть показуватися в кулінарній книзі.",
|
||||||
"hide-cookbooks-from-other-households": "Hide Cookbooks from Other Households",
|
"hide-cookbooks-from-other-households": "Приховати кулінарні книги від інших сімей",
|
||||||
"hide-cookbooks-from-other-households-description": "When enabled, only cookbooks from your household will appear on the sidebar",
|
"hide-cookbooks-from-other-households-description": "Якщо вибрано, тільки кулінарні книги вашої сім'ї буде видно на боковій панелі",
|
||||||
"public-cookbook": "Публічна кулінарна книга",
|
"public-cookbook": "Публічна кулінарна книга",
|
||||||
"public-cookbook-description": "Публічними кулінарними книгами можна поділитися з будь-ким, і вони будуть відображатися на сторінці вашої групи.",
|
"public-cookbook-description": "Публічними кулінарними книгами можна поділитися з будь-ким, і вони будуть відображатися на сторінці вашої групи.",
|
||||||
"filter-options": "Параметри фільтра",
|
"filter-options": "Параметри фільтра",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "我做了这道菜",
|
"made-this": "我做了这道菜",
|
||||||
"how-did-it-turn-out": "成品怎么样?",
|
"how-did-it-turn-out": "成品怎么样?",
|
||||||
"user-made-this": "{user}做了",
|
"user-made-this": "{user}做了",
|
||||||
"last-made-date": "上次制作于{date}",
|
|
||||||
"api-extras-description": "食谱扩展是Mealie API的关键功能之一。它允许你在食谱中添加自定义JSON键值对,以供第三方程序使用。你可以利用这些键提供信息,实现更多功能,例如触发自动化,或转发自定义信息到指定的设备上。",
|
"api-extras-description": "食谱扩展是Mealie API的关键功能之一。它允许你在食谱中添加自定义JSON键值对,以供第三方程序使用。你可以利用这些键提供信息,实现更多功能,例如触发自动化,或转发自定义信息到指定的设备上。",
|
||||||
"message-key": "键名",
|
"message-key": "键名",
|
||||||
"parse": "自动解析",
|
"parse": "自动解析",
|
||||||
|
|||||||
@@ -579,7 +579,6 @@
|
|||||||
"made-this": "I Made This",
|
"made-this": "I Made This",
|
||||||
"how-did-it-turn-out": "How did it turn out?",
|
"how-did-it-turn-out": "How did it turn out?",
|
||||||
"user-made-this": "{user} made this",
|
"user-made-this": "{user} made this",
|
||||||
"last-made-date": "Last Made {date}",
|
|
||||||
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
|
||||||
"message-key": "Message Key",
|
"message-key": "Message Key",
|
||||||
"parse": "Parse",
|
"parse": "Parse",
|
||||||
|
|||||||
@@ -391,6 +391,11 @@ export interface CreateIngredientFoodAlias {
|
|||||||
name: string;
|
name: string;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
|
export interface ShoppingListAddRecipeParamsBulk {
|
||||||
|
recipeIncrementQuantity?: number;
|
||||||
|
recipeIngredients?: RecipeIngredient[] | null;
|
||||||
|
recipeId: string;
|
||||||
|
}
|
||||||
export interface ShoppingListCreate {
|
export interface ShoppingListCreate {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
extras?: {
|
extras?: {
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ export interface UserBase {
|
|||||||
id: string;
|
id: string;
|
||||||
username?: string | null;
|
username?: string | null;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
|
fullName?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCategoryResponse {
|
export interface RecipeCategoryResponse {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -93,6 +93,12 @@ export interface GroupSummary {
|
|||||||
slug: string;
|
slug: string;
|
||||||
preferences?: ReadGroupPreferences | null;
|
preferences?: ReadGroupPreferences | null;
|
||||||
}
|
}
|
||||||
|
export interface LongLiveTokenCreateResponse {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
createdAt?: string | null;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
export interface LongLiveTokenIn {
|
export interface LongLiveTokenIn {
|
||||||
name: string;
|
name: string;
|
||||||
integrationId?: string;
|
integrationId?: string;
|
||||||
@@ -130,7 +136,6 @@ export interface PrivateUser {
|
|||||||
lockedAt?: string | null;
|
lockedAt?: string | null;
|
||||||
}
|
}
|
||||||
export interface LongLiveTokenOut {
|
export interface LongLiveTokenOut {
|
||||||
token: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BaseCRUDAPI } from "../base/base-clients";
|
import { BaseCRUDAPI } from "../base/base-clients";
|
||||||
import { RecipeIngredient } from "../types/recipe";
|
|
||||||
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
import {
|
import {
|
||||||
|
ShoppingListAddRecipeParamsBulk,
|
||||||
ShoppingListCreate,
|
ShoppingListCreate,
|
||||||
ShoppingListItemCreate,
|
ShoppingListItemCreate,
|
||||||
ShoppingListItemOut,
|
ShoppingListItemOut,
|
||||||
@@ -16,7 +16,7 @@ const prefix = "/api";
|
|||||||
const routes = {
|
const routes = {
|
||||||
shoppingLists: `${prefix}/households/shopping/lists`,
|
shoppingLists: `${prefix}/households/shopping/lists`,
|
||||||
shoppingListsId: (id: string) => `${prefix}/households/shopping/lists/${id}`,
|
shoppingListsId: (id: string) => `${prefix}/households/shopping/lists/${id}`,
|
||||||
shoppingListIdAddRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}`,
|
shoppingListIdAddRecipe: (id: string) => `${prefix}/households/shopping/lists/${id}/recipe`,
|
||||||
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
||||||
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/households/shopping/lists/${id}/label-settings`,
|
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/households/shopping/lists/${id}/label-settings`,
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingLi
|
|||||||
baseRoute = routes.shoppingLists;
|
baseRoute = routes.shoppingLists;
|
||||||
itemRoute = routes.shoppingListsId;
|
itemRoute = routes.shoppingListsId;
|
||||||
|
|
||||||
async addRecipe(itemId: string, recipeId: string, recipeIncrementQuantity = 1, recipeIngredients: RecipeIngredient[] | null = null) {
|
async addRecipes(itemId: string, data: ShoppingListAddRecipeParamsBulk[]) {
|
||||||
return await this.requests.post(routes.shoppingListIdAddRecipe(itemId, recipeId), { recipeIncrementQuantity, recipeIngredients });
|
return await this.requests.post(routes.shoppingListIdAddRecipe(itemId), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeRecipe(itemId: string, recipeId: string, recipeDecrementQuantity = 1) {
|
async removeRecipe(itemId: string, recipeId: string, recipeDecrementQuantity = 1) {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export type RecipeSearchQuery = {
|
|||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
orderBy?: string;
|
orderBy?: string;
|
||||||
|
orderByNullPosition?: "first" | "last";
|
||||||
|
|
||||||
_searchSeed?: string;
|
_searchSeed?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ import {
|
|||||||
mdiFileCabinet,
|
mdiFileCabinet,
|
||||||
mdiSilverwareForkKnife,
|
mdiSilverwareForkKnife,
|
||||||
mdiCodeTags,
|
mdiCodeTags,
|
||||||
|
mdiKnife,
|
||||||
|
mdiCookie
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
|
||||||
export const icons = {
|
export const icons = {
|
||||||
@@ -271,6 +273,8 @@ export const icons = {
|
|||||||
windowClose: mdiWindowClose,
|
windowClose: mdiWindowClose,
|
||||||
zip: mdiFolderZipOutline,
|
zip: mdiFolderZipOutline,
|
||||||
undo: mdiUndo,
|
undo: mdiUndo,
|
||||||
|
knfife: mdiKnife,
|
||||||
|
bread: mdiCookie,
|
||||||
|
|
||||||
// Crud
|
// Crud
|
||||||
backArrow: mdiArrowLeftBoldOutline,
|
backArrow: mdiArrowLeftBoldOutline,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mealie",
|
"name": "mealie",
|
||||||
"version": "2.4.2",
|
"version": "2.7.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt",
|
"dev": "nuxt",
|
||||||
|
|||||||
@@ -17,11 +17,69 @@
|
|||||||
<v-container>
|
<v-container>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="3" class="text-left">
|
<v-col cols="3" class="text-left">
|
||||||
<ButtonLink :to="`/shopping-lists?disableRedirect=true`" :text="$tc('general.back')" :icon="$globals.icons.backArrow" />
|
<ButtonLink :to="`/shopping-lists?disableRedirect=true`" :text="$tc('shopping-list.all-lists')" :icon="$globals.icons.backArrow" />
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="6" class="d-flex justify-center">
|
<v-col cols="6" class="d-none d-lg-flex justify-center">
|
||||||
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/shopping-cart.svg')"></v-img>
|
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/shopping-cart.svg')"></v-img>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col class="d-flex justify-end">
|
||||||
|
<BaseButtonGroup
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
icon: $globals.icons.contentCopy,
|
||||||
|
text: '',
|
||||||
|
event: 'edit',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
icon: $globals.icons.contentCopy,
|
||||||
|
text: $tc('shopping-list.copy-as-text'),
|
||||||
|
event: 'copy-plain',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.contentCopy,
|
||||||
|
text: $tc('shopping-list.copy-as-markdown'),
|
||||||
|
event: 'copy-markdown',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.checkboxOutline,
|
||||||
|
text: $tc('shopping-list.check-all-items'),
|
||||||
|
event: 'check',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.dotsVertical,
|
||||||
|
text: '',
|
||||||
|
event: 'three-dot',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
icon: $globals.icons.tags,
|
||||||
|
text: $tc('shopping-list.toggle-label-sort'),
|
||||||
|
event: 'sort-by-labels',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.tags,
|
||||||
|
text: $tc('shopping-list.reorder-labels'),
|
||||||
|
event: 'reorder-labels',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.tags,
|
||||||
|
text: $tc('shopping-list.manage-labels'),
|
||||||
|
event: 'manage-labels',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@edit="edit = true"
|
||||||
|
@three-dot="threeDot = true"
|
||||||
|
@check="openCheckAll"
|
||||||
|
@sort-by-labels="sortByLabels"
|
||||||
|
@copy-plain="copyListItems('plain')"
|
||||||
|
@copy-markdown="copyListItems('markdown')"
|
||||||
|
@reorder-labels="toggleReorderLabelsDialog()"
|
||||||
|
@manage-labels="$router.push(`/group/data/labels`)"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -93,6 +151,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Create Item -->
|
||||||
|
<div v-if="createEditorOpen">
|
||||||
|
<ShoppingListItemEditor
|
||||||
|
v-model="createListItemData"
|
||||||
|
class="my-4"
|
||||||
|
:labels="allLabels || []"
|
||||||
|
:units="allUnits || []"
|
||||||
|
:foods="allFoods || []"
|
||||||
|
:allow-delete="false"
|
||||||
|
@delete="createEditorOpen = false"
|
||||||
|
@cancel="createEditorOpen = false"
|
||||||
|
@save="createListItem"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else class="d-flex justify-end">
|
||||||
|
<BaseButton create @click="createEditorOpen = true" > {{ $t('general.add') }} </BaseButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Reorder Labels -->
|
<!-- Reorder Labels -->
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
v-model="reorderLabelsDialog"
|
v-model="reorderLabelsDialog"
|
||||||
@@ -119,113 +195,38 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
||||||
<!-- Settings -->
|
|
||||||
<BaseDialog
|
|
||||||
v-model="settingsDialog"
|
|
||||||
:icon="$globals.icons.cog"
|
|
||||||
:title="$t('general.settings')"
|
|
||||||
@confirm="updateSettings"
|
|
||||||
>
|
|
||||||
<v-container>
|
|
||||||
<v-form>
|
|
||||||
<v-select
|
|
||||||
v-model="currentUserId"
|
|
||||||
:items="allUsers"
|
|
||||||
item-text="fullName"
|
|
||||||
item-value="id"
|
|
||||||
:label="$t('general.owner')"
|
|
||||||
:prepend-icon="$globals.icons.user"
|
|
||||||
/>
|
|
||||||
</v-form>
|
|
||||||
</v-container>
|
|
||||||
</BaseDialog>
|
|
||||||
|
|
||||||
<!-- Create Item -->
|
|
||||||
<div v-if="createEditorOpen">
|
|
||||||
<ShoppingListItemEditor
|
|
||||||
v-model="createListItemData"
|
|
||||||
class="my-4"
|
|
||||||
:labels="allLabels || []"
|
|
||||||
:units="allUnits || []"
|
|
||||||
:foods="allFoods || []"
|
|
||||||
@delete="createEditorOpen = false"
|
|
||||||
@cancel="createEditorOpen = false"
|
|
||||||
@save="createListItem"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="mt-4 d-flex justify-end">
|
|
||||||
<BaseButton
|
|
||||||
v-if="preferences.viewByLabel" edit class="mr-2"
|
|
||||||
:disabled="$nuxt.isOffline"
|
|
||||||
@click="toggleReorderLabelsDialog">
|
|
||||||
<template #icon> {{ $globals.icons.tags }} </template>
|
|
||||||
{{ $t('shopping-list.reorder-labels') }}
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton create @click="createEditorOpen = true" > {{ $t('general.add') }} </BaseButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Action Bar -->
|
|
||||||
<div class="d-flex justify-end mb-4 mt-2">
|
|
||||||
<BaseButtonGroup
|
|
||||||
:buttons="[
|
|
||||||
{
|
|
||||||
icon: $globals.icons.contentCopy,
|
|
||||||
text: '',
|
|
||||||
event: 'edit',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
icon: $globals.icons.contentCopy,
|
|
||||||
text: $tc('shopping-list.copy-as-text'),
|
|
||||||
event: 'copy-plain',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.contentCopy,
|
|
||||||
text: $tc('shopping-list.copy-as-markdown'),
|
|
||||||
event: 'copy-markdown',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.delete,
|
|
||||||
text: $tc('shopping-list.delete-checked'),
|
|
||||||
event: 'delete',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.tags,
|
|
||||||
text: $tc('shopping-list.toggle-label-sort'),
|
|
||||||
event: 'sort-by-labels',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.checkboxBlankOutline,
|
|
||||||
text: $tc('shopping-list.uncheck-all-items'),
|
|
||||||
event: 'uncheck',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.checkboxOutline,
|
|
||||||
text: $tc('shopping-list.check-all-items'),
|
|
||||||
event: 'check',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
@edit="edit = true"
|
|
||||||
@delete="openDeleteChecked"
|
|
||||||
@uncheck="openUncheckAll"
|
|
||||||
@check="openCheckAll"
|
|
||||||
@sort-by-labels="sortByLabels"
|
|
||||||
@copy-plain="copyListItems('plain')"
|
|
||||||
@copy-markdown="copyListItems('markdown')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Checked Items -->
|
<!-- Checked Items -->
|
||||||
<div v-if="listItems.checked && listItems.checked.length > 0" class="mt-6">
|
<div v-if="listItems.checked && listItems.checked.length > 0" class="mt-6">
|
||||||
<button @click="toggleShowChecked()">
|
<div class="d-flex">
|
||||||
<span>
|
<div class="flex-grow-1">
|
||||||
<v-icon>
|
<button @click="toggleShowChecked()">
|
||||||
{{ showChecked ? $globals.icons.chevronDown : $globals.icons.chevronRight }}
|
<span>
|
||||||
</v-icon>
|
<v-icon>
|
||||||
</span>
|
{{ showChecked ? $globals.icons.chevronDown : $globals.icons.chevronRight }}
|
||||||
{{ $tc('shopping-list.items-checked-count', listItems.checked ? listItems.checked.length : 0) }}
|
</v-icon>
|
||||||
</button>
|
</span>
|
||||||
|
{{ $tc('shopping-list.items-checked-count', listItems.checked ? listItems.checked.length : 0) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="justify-end mt-n2">
|
||||||
|
<BaseButtonGroup
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
icon: $globals.icons.checkboxBlankOutline,
|
||||||
|
text: $tc('shopping-list.uncheck-all-items'),
|
||||||
|
event: 'uncheck',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.delete,
|
||||||
|
text: $tc('shopping-list.delete-checked'),
|
||||||
|
event: 'delete',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@uncheck="openUncheckAll"
|
||||||
|
@delete="openDeleteChecked"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<v-divider class="my-4"></v-divider>
|
<v-divider class="my-4"></v-divider>
|
||||||
<v-expand-transition>
|
<v-expand-transition>
|
||||||
<div v-show="showChecked">
|
<div v-show="showChecked">
|
||||||
@@ -277,29 +278,6 @@
|
|||||||
</RecipeList>
|
</RecipeList>
|
||||||
</section>
|
</section>
|
||||||
</v-lazy>
|
</v-lazy>
|
||||||
|
|
||||||
<v-lazy>
|
|
||||||
<div class="d-flex justify-end">
|
|
||||||
<BaseButton
|
|
||||||
edit
|
|
||||||
:disabled="$nuxt.isOffline"
|
|
||||||
@click="toggleSettingsDialog"
|
|
||||||
>
|
|
||||||
<template #icon> {{ $globals.icons.cog }} </template>
|
|
||||||
{{ $t('general.settings') }}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
</v-lazy>
|
|
||||||
|
|
||||||
<v-lazy>
|
|
||||||
<div v-if="$nuxt.isOnline" class="d-flex justify-end mt-10">
|
|
||||||
<ButtonLink
|
|
||||||
:to="`/group/data/labels`"
|
|
||||||
:text="$tc('shopping-list.manage-labels')"
|
|
||||||
:icon="$globals.icons.tags"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-lazy>
|
|
||||||
<WakelockSwitch/>
|
<WakelockSwitch/>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -314,7 +292,6 @@ import { useUserApi } from "~/composables/api";
|
|||||||
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
||||||
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
||||||
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/household";
|
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/household";
|
||||||
import { UserOut } from "~/lib/api/types/user";
|
|
||||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||||
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
||||||
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
||||||
@@ -349,8 +326,8 @@ export default defineComponent({
|
|||||||
const userApi = useUserApi();
|
const userApi = useUserApi();
|
||||||
|
|
||||||
const edit = ref(false);
|
const edit = ref(false);
|
||||||
|
const threeDot = ref(false);
|
||||||
const reorderLabelsDialog = ref(false);
|
const reorderLabelsDialog = ref(false);
|
||||||
const settingsDialog = ref(false);
|
|
||||||
const preserveItemOrder = ref(false);
|
const preserveItemOrder = ref(false);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -678,13 +655,6 @@ export default defineComponent({
|
|||||||
localLabels.value = shoppingList.value?.labelSettings
|
localLabels.value = shoppingList.value?.labelSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleSettingsDialog() {
|
|
||||||
if (!settingsDialog.value) {
|
|
||||||
await fetchAllUsers();
|
|
||||||
}
|
|
||||||
settingsDialog.value = !settingsDialog.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLabelOrder(labelSettings: ShoppingListMultiPurposeLabelOut[]) {
|
function updateLabelOrder(labelSettings: ShoppingListMultiPurposeLabelOut[]) {
|
||||||
if (!shoppingList.value) {
|
if (!shoppingList.value) {
|
||||||
return;
|
return;
|
||||||
@@ -864,7 +834,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
loadingCounter.value += 1;
|
loadingCounter.value += 1;
|
||||||
recipeReferenceLoading.value = true;
|
recipeReferenceLoading.value = true;
|
||||||
const { data } = await userApi.shopping.lists.addRecipe(shoppingList.value.id, recipeId);
|
const { data } = await userApi.shopping.lists.addRecipes(shoppingList.value.id, [{ recipeId }]);
|
||||||
recipeReferenceLoading.value = false;
|
recipeReferenceLoading.value = false;
|
||||||
loadingCounter.value -= 1;
|
loadingCounter.value -= 1;
|
||||||
|
|
||||||
@@ -1064,39 +1034,6 @@ export default defineComponent({
|
|||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
|
||||||
// Shopping List Settings
|
|
||||||
|
|
||||||
const allUsers = ref<UserOut[]>([]);
|
|
||||||
const currentUserId = ref<string | undefined>();
|
|
||||||
async function fetchAllUsers() {
|
|
||||||
const { data } = await userApi.households.fetchMembers();
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update current user
|
|
||||||
allUsers.value = data.items.sort((a, b) => ((a.fullName || "") < (b.fullName || "") ? -1 : 1));
|
|
||||||
currentUserId.value = shoppingList.value?.userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateSettings() {
|
|
||||||
if (!shoppingList.value || !currentUserId.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingCounter.value += 1;
|
|
||||||
const { data } = await userApi.shopping.lists.updateOne(
|
|
||||||
shoppingList.value.id,
|
|
||||||
{...shoppingList.value, userId: currentUserId.value},
|
|
||||||
);
|
|
||||||
loadingCounter.value -= 1;
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
addRecipeReferenceToList,
|
addRecipeReferenceToList,
|
||||||
@@ -1112,6 +1049,7 @@ export default defineComponent({
|
|||||||
openDeleteChecked,
|
openDeleteChecked,
|
||||||
deleteListItem,
|
deleteListItem,
|
||||||
edit,
|
edit,
|
||||||
|
threeDot,
|
||||||
getLabelColor,
|
getLabelColor,
|
||||||
groupSlug,
|
groupSlug,
|
||||||
itemsByLabel,
|
itemsByLabel,
|
||||||
@@ -1123,8 +1061,6 @@ export default defineComponent({
|
|||||||
removeRecipeReferenceToList,
|
removeRecipeReferenceToList,
|
||||||
reorderLabelsDialog,
|
reorderLabelsDialog,
|
||||||
toggleReorderLabelsDialog,
|
toggleReorderLabelsDialog,
|
||||||
settingsDialog,
|
|
||||||
toggleSettingsDialog,
|
|
||||||
localLabels,
|
localLabels,
|
||||||
updateLabelOrder,
|
updateLabelOrder,
|
||||||
cancelLabelOrder,
|
cancelLabelOrder,
|
||||||
@@ -1144,9 +1080,6 @@ export default defineComponent({
|
|||||||
updateIndexUncheckedByLabel,
|
updateIndexUncheckedByLabel,
|
||||||
allUnits,
|
allUnits,
|
||||||
allFoods,
|
allFoods,
|
||||||
allUsers,
|
|
||||||
currentUserId,
|
|
||||||
updateSettings,
|
|
||||||
getTextColor,
|
getTextColor,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,27 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
||||||
|
<!-- Settings -->
|
||||||
|
<BaseDialog
|
||||||
|
v-model="ownerDialog"
|
||||||
|
:icon="$globals.icons.admin"
|
||||||
|
:title="$t('user.edit-user')"
|
||||||
|
@confirm="updateOwner"
|
||||||
|
>
|
||||||
|
<v-container>
|
||||||
|
<v-form>
|
||||||
|
<v-select
|
||||||
|
v-model="updateUserId"
|
||||||
|
:items="allUsers"
|
||||||
|
item-text="fullName"
|
||||||
|
item-value="id"
|
||||||
|
:label="$t('general.owner')"
|
||||||
|
:prepend-icon="$globals.icons.user"
|
||||||
|
/>
|
||||||
|
</v-form>
|
||||||
|
</v-container>
|
||||||
|
</BaseDialog>
|
||||||
|
|
||||||
<BaseDialog v-model="deleteDialog" :title="$tc('general.confirm')" color="error" @confirm="deleteOne">
|
<BaseDialog v-model="deleteDialog" :title="$tc('general.confirm')" color="error" @confirm="deleteOne">
|
||||||
<v-card-text>{{ $t('shopping-list.are-you-sure-you-want-to-delete-this-item') }}</v-card-text>
|
<v-card-text>{{ $t('shopping-list.are-you-sure-you-want-to-delete-this-item') }}</v-card-text>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
@@ -38,26 +59,34 @@
|
|||||||
<v-icon left>
|
<v-icon left>
|
||||||
{{ $globals.icons.cartCheck }}
|
{{ $globals.icons.cartCheck }}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
{{ list.name }}
|
<div class="flex-grow-1">
|
||||||
<v-btn class="ml-auto" icon @click.prevent="openDelete(list.id)">
|
{{ list.name }}
|
||||||
<v-icon>
|
</div>
|
||||||
{{ $globals.icons.delete }}
|
<div class="d-flex justify-end">
|
||||||
</v-icon>
|
<v-btn icon @click.prevent="toggleOwnerDialog(list)">
|
||||||
</v-btn>
|
<v-icon>
|
||||||
|
{{ $globals.icons.user }}
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon @click.prevent="openDelete(list.id)">
|
||||||
|
<v-icon>
|
||||||
|
{{ $globals.icons.delete }}
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
</v-card>
|
</v-card>
|
||||||
</section>
|
</section>
|
||||||
<div class="d-flex justify-end mt-10">
|
|
||||||
<ButtonLink :to="`/group/data/labels`" :text="$tc('shopping-list.manage-labels')" :icon="$globals.icons.tags" />
|
|
||||||
</div>
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, useAsync, useContext, reactive, ref, toRefs, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useAsync, useContext, reactive, ref, toRefs, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
|
||||||
|
import { ShoppingListOut } from "~/lib/api/types/household";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useAsyncKey } from "~/composables/use-utils";
|
import { useAsyncKey } from "~/composables/use-utils";
|
||||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
||||||
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
middleware: "auth",
|
middleware: "auth",
|
||||||
@@ -77,6 +106,8 @@ export default defineComponent({
|
|||||||
createDialog: false,
|
createDialog: false,
|
||||||
deleteDialog: false,
|
deleteDialog: false,
|
||||||
deleteTarget: "",
|
deleteTarget: "",
|
||||||
|
ownerDialog: false,
|
||||||
|
ownerTarget: ref<ShoppingListOut | null>(null),
|
||||||
});
|
});
|
||||||
|
|
||||||
const shoppingLists = useAsync(async () => {
|
const shoppingLists = useAsync(async () => {
|
||||||
@@ -136,6 +167,53 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function toggleOwnerDialog(list: ShoppingListOut) {
|
||||||
|
if (!state.ownerDialog) {
|
||||||
|
state.ownerTarget = list;
|
||||||
|
await fetchAllUsers();
|
||||||
|
}
|
||||||
|
state.ownerDialog = !state.ownerDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Shopping List Edit User/Owner
|
||||||
|
|
||||||
|
const allUsers = ref<UserOut[]>([]);
|
||||||
|
const updateUserId = ref<string | undefined>();
|
||||||
|
async function fetchAllUsers() {
|
||||||
|
const { data } = await userApi.households.fetchMembers();
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update current user
|
||||||
|
allUsers.value = data.items.sort((a, b) => ((a.fullName || "") < (b.fullName || "") ? -1 : 1));
|
||||||
|
updateUserId.value = state.ownerTarget?.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateOwner() {
|
||||||
|
if (!state.ownerTarget || !updateUserId.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// user has not changed, so we should not update
|
||||||
|
if (state.ownerTarget.userId === updateUserId.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// get full list, so the move does not delete shopping list items
|
||||||
|
const { data: fullList } = await userApi.shopping.lists.getOne(state.ownerTarget.id);
|
||||||
|
if (!fullList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { data } = await userApi.shopping.lists.updateOne(
|
||||||
|
state.ownerTarget.id,
|
||||||
|
{...fullList, userId: updateUserId.value},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openDelete(id: string) {
|
function openDelete(id: string) {
|
||||||
state.deleteDialog = true;
|
state.deleteDialog = true;
|
||||||
state.deleteTarget = id;
|
state.deleteTarget = id;
|
||||||
@@ -155,6 +233,10 @@ export default defineComponent({
|
|||||||
preferences,
|
preferences,
|
||||||
shoppingListChoices,
|
shoppingListChoices,
|
||||||
createOne,
|
createOne,
|
||||||
|
toggleOwnerDialog,
|
||||||
|
allUsers,
|
||||||
|
updateUserId,
|
||||||
|
updateOwner,
|
||||||
deleteOne,
|
deleteOne,
|
||||||
openDelete,
|
openDelete,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
"""remove instructions index
|
||||||
|
|
||||||
|
Revision ID: 7cf3054cbbcc
|
||||||
|
Revises: b9e516e2d3b3
|
||||||
|
Create Date: 2025-02-09 15:31:00.772295
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
|
from alembic import op
|
||||||
|
from mealie.db.models._model_utils.guid import GUID
|
||||||
|
from mealie.core.root_logger import get_logger
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "7cf3054cbbcc"
|
||||||
|
down_revision: str | None = "b9e516e2d3b3"
|
||||||
|
branch_labels: str | tuple[str, ...] | None = None
|
||||||
|
depends_on: str | tuple[str, ...] | None = None
|
||||||
|
|
||||||
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls) -> list[orm.InstrumentedAttribute]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class RecipeModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "recipes"
|
||||||
|
|
||||||
|
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||||
|
name_normalized: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False, index=True)
|
||||||
|
description_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [cls.name_normalized, cls.description_normalized]
|
||||||
|
|
||||||
|
|
||||||
|
class RecipeIngredientModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "recipes_ingredients"
|
||||||
|
|
||||||
|
id: orm.Mapped[int] = orm.mapped_column(sa.Integer, primary_key=True)
|
||||||
|
note_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
original_text_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [cls.note_normalized, cls.original_text_normalized]
|
||||||
|
|
||||||
|
|
||||||
|
class IngredientFoodModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "ingredient_foods"
|
||||||
|
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||||
|
name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
plural_name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [cls.name_normalized, cls.plural_name_normalized]
|
||||||
|
|
||||||
|
|
||||||
|
class IngredientFoodAliasModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "ingredient_foods_aliases"
|
||||||
|
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||||
|
name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [cls.name_normalized]
|
||||||
|
|
||||||
|
|
||||||
|
class IngredientUnitModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "ingredient_units"
|
||||||
|
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||||
|
name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
plural_name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
abbreviation_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
plural_abbreviation_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [
|
||||||
|
cls.name_normalized,
|
||||||
|
cls.plural_name_normalized,
|
||||||
|
cls.abbreviation_normalized,
|
||||||
|
cls.plural_abbreviation_normalized,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IngredientUnitAliasModel(SqlAlchemyBase):
|
||||||
|
__tablename__ = "ingredient_units_aliases"
|
||||||
|
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||||
|
name_normalized: orm.Mapped[str | None] = orm.mapped_column(sa.String, index=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def normalized_fields(cls):
|
||||||
|
return [cls.name_normalized]
|
||||||
|
|
||||||
|
|
||||||
|
def truncate_normalized_fields() -> None:
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = orm.Session(bind=bind)
|
||||||
|
|
||||||
|
models: list[type[SqlAlchemyBase]] = [
|
||||||
|
RecipeModel,
|
||||||
|
RecipeIngredientModel,
|
||||||
|
IngredientFoodModel,
|
||||||
|
IngredientFoodAliasModel,
|
||||||
|
IngredientUnitModel,
|
||||||
|
IngredientUnitAliasModel,
|
||||||
|
]
|
||||||
|
|
||||||
|
for model in models:
|
||||||
|
for record in session.query(model).all():
|
||||||
|
for field in model.normalized_fields():
|
||||||
|
if not (field_value := getattr(record, field.key)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
setattr(record, field.key, field_value[:255])
|
||||||
|
|
||||||
|
try:
|
||||||
|
session.commit()
|
||||||
|
except Exception:
|
||||||
|
logger.exception(f"Failed to truncate normalized fields for {model.__name__}")
|
||||||
|
session.rollback()
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||||
|
batch_op.drop_index("ix_recipe_instructions_text")
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
truncate_normalized_fields()
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||||
|
batch_op.create_index("ix_recipe_instructions_text", ["text"], unique=False)
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -1,3 +1,12 @@
|
|||||||
|
import re
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
# pyrdfa3 is no longer being updated and has docstrings that emit syntax warnings
|
||||||
|
warnings.filterwarnings(
|
||||||
|
"ignore", module=".*pyRdfa", category=SyntaxWarning, message=re.escape("invalid escape sequence '\\-'")
|
||||||
|
)
|
||||||
|
|
||||||
|
# ruff: noqa: E402
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
@@ -115,6 +124,7 @@ register_debug_handler(app)
|
|||||||
|
|
||||||
async def start_scheduler():
|
async def start_scheduler():
|
||||||
SchedulerRegistry.register_daily(
|
SchedulerRegistry.register_daily(
|
||||||
|
tasks.purge_expired_tokens,
|
||||||
tasks.purge_group_registration,
|
tasks.purge_group_registration,
|
||||||
tasks.purge_password_reset_tokens,
|
tasks.purge_password_reset_tokens,
|
||||||
tasks.purge_group_data_exports,
|
tasks.purge_group_data_exports,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|||||||
from mealie.core.settings.themes import Theme
|
from mealie.core.settings.themes import Theme
|
||||||
|
|
||||||
from .db_providers import AbstractDBProvider, db_provider_factory
|
from .db_providers import AbstractDBProvider, db_provider_factory
|
||||||
|
from .static import PACKAGE_DIR
|
||||||
|
|
||||||
|
|
||||||
class ScheduleTime(NamedTuple):
|
class ScheduleTime(NamedTuple):
|
||||||
@@ -109,7 +110,7 @@ class AppSettings(AppLoggingSettings):
|
|||||||
BASE_URL: str = "http://localhost:8080"
|
BASE_URL: str = "http://localhost:8080"
|
||||||
"""trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)"""
|
"""trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)"""
|
||||||
|
|
||||||
STATIC_FILES: str = ""
|
STATIC_FILES: str = str(PACKAGE_DIR / "frontend")
|
||||||
"""path to static files directory (ex. `mealie/dist`)"""
|
"""path to static files directory (ex. `mealie/dist`)"""
|
||||||
|
|
||||||
IS_DEMO: bool = False
|
IS_DEMO: bool = False
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ from mealie import __version__
|
|||||||
APP_VERSION = __version__
|
APP_VERSION = __version__
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
|
PACKAGE_DIR = CWD.parent.parent
|
||||||
BASE_DIR = CWD.parent.parent.parent
|
BASE_DIR = CWD.parent.parent.parent
|
||||||
|
|||||||
@@ -69,16 +69,16 @@ def db_is_at_head(alembic_cfg: config.Config) -> bool:
|
|||||||
def safe_try(func: Callable):
|
def safe_try(func: Callable):
|
||||||
try:
|
try:
|
||||||
func()
|
func()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(f"Error calling '{func.__name__}': {e}")
|
logger.exception(f"Error calling '{func.__name__}'")
|
||||||
|
|
||||||
|
|
||||||
def connect(session: orm.Session) -> bool:
|
def connect(session: orm.Session) -> bool:
|
||||||
try:
|
try:
|
||||||
session.execute(text("SELECT 1"))
|
session.execute(text("SELECT 1"))
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(f"Error connecting to database: {e}")
|
logger.exception("Error connecting to database")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -106,23 +106,27 @@ def main():
|
|||||||
if not os.path.isfile(alembic_cfg_path):
|
if not os.path.isfile(alembic_cfg_path):
|
||||||
raise Exception("Provided alembic config path doesn't exist")
|
raise Exception("Provided alembic config path doesn't exist")
|
||||||
|
|
||||||
|
run_fixes = False
|
||||||
alembic_cfg = Config(alembic_cfg_path)
|
alembic_cfg = Config(alembic_cfg_path)
|
||||||
if db_is_at_head(alembic_cfg):
|
if db_is_at_head(alembic_cfg):
|
||||||
logger.debug("Migration not needed.")
|
logger.debug("Migration not needed.")
|
||||||
else:
|
else:
|
||||||
logger.info("Migration needed. Performing migration...")
|
logger.info("Migration needed. Performing migration...")
|
||||||
command.upgrade(alembic_cfg, "head")
|
command.upgrade(alembic_cfg, "head")
|
||||||
|
run_fixes = True
|
||||||
|
|
||||||
if session.get_bind().name == "postgresql": # needed for fuzzy search and fast GIN text indices
|
if session.get_bind().name == "postgresql": # needed for fuzzy search and fast GIN text indices
|
||||||
session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm;"))
|
session.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm;"))
|
||||||
|
|
||||||
db = get_repositories(session, group_id=None, household_id=None)
|
db = get_repositories(session, group_id=None, household_id=None)
|
||||||
safe_try(lambda: fix_migration_data(session))
|
|
||||||
safe_try(lambda: fix_slug_food_names(db))
|
|
||||||
safe_try(lambda: fix_group_with_no_name(session))
|
|
||||||
|
|
||||||
if db.users.get_all():
|
if db.users.get_all():
|
||||||
logger.debug("Database exists")
|
logger.debug("Database exists")
|
||||||
|
if run_fixes:
|
||||||
|
safe_try(lambda: fix_migration_data(session))
|
||||||
|
safe_try(lambda: fix_slug_food_names(db))
|
||||||
|
safe_try(lambda: fix_group_with_no_name(session))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.info("Database contains no users, initializing...")
|
logger.info("Database contains no users, initializing...")
|
||||||
init_db(session)
|
init_db(session)
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ class SqlAlchemyBase(DeclarativeBase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def normalize(cls, val: str) -> str:
|
def normalize(cls, val: str) -> str:
|
||||||
return unidecode(val).lower().strip()
|
# We cap the length to 255 to prevent indexes from being too long; see:
|
||||||
|
# https://www.postgresql.org/docs/current/btree.html
|
||||||
|
return unidecode(val).lower().strip()[:255]
|
||||||
|
|
||||||
|
|
||||||
class BaseMixins:
|
class BaseMixins:
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class RecipeInstruction(SqlAlchemyBase):
|
|||||||
recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True)
|
recipe_id: Mapped[GUID | None] = mapped_column(GUID, ForeignKey("recipes.id"), index=True)
|
||||||
position: Mapped[int | None] = mapped_column(Integer, index=True)
|
position: Mapped[int | None] = mapped_column(Integer, index=True)
|
||||||
type: Mapped[str | None] = mapped_column(String, default="")
|
type: Mapped[str | None] = mapped_column(String, default="")
|
||||||
title: Mapped[str | None] = mapped_column(String) # This is the section title!!!
|
title: Mapped[str | None] = mapped_column(String) # This is the section title
|
||||||
text: Mapped[str | None] = mapped_column(String, index=True)
|
text: Mapped[str | None] = mapped_column(String)
|
||||||
summary: Mapped[str | None] = mapped_column(String)
|
summary: Mapped[str | None] = mapped_column(String)
|
||||||
|
|
||||||
ingredient_references: Mapped[list[RecipeIngredientRefLink]] = orm.relationship(
|
ingredient_references: Mapped[list[RecipeIngredientRefLink]] = orm.relationship(
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
"generic-updated": "{name} blev opdateret",
|
"generic-updated": "{name} blev opdateret",
|
||||||
"generic-created-with-url": "{name} er oprettet, {url}",
|
"generic-created-with-url": "{name} er oprettet, {url}",
|
||||||
"generic-updated-with-url": "{name} er blevet opdateret, {url}",
|
"generic-updated-with-url": "{name} er blevet opdateret, {url}",
|
||||||
"generic-duplicated": "{name} er blevet dublikeret",
|
"generic-duplicated": "{name} er blevet duplikeret",
|
||||||
"generic-deleted": "{name} er blevet slettet"
|
"generic-deleted": "{name} er blevet slettet"
|
||||||
},
|
},
|
||||||
"datetime": {
|
"datetime": {
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"header_text": "Glemt adgangskode",
|
"header_text": "Glemt adgangskode",
|
||||||
"message_top": "Du har anmodet om at nulstille din adgangskode.",
|
"message_top": "Du har anmodet om at nulstille din adgangskode.",
|
||||||
"message_bottom": "Klik på knappen ovenfor for at nulstille din adgangskode.",
|
"message_bottom": "Klik på knappen ovenfor for at nulstille din adgangskode.",
|
||||||
"button_text": "Nulstil adgangskoden"
|
"button_text": "Nulstil adgangskode"
|
||||||
},
|
},
|
||||||
"invitation": {
|
"invitation": {
|
||||||
"subject": "Invitation til at deltage i Mealie",
|
"subject": "Invitation til at deltage i Mealie",
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Reseptien nimien täytyy olla yksilöllisiä",
|
"unique-name-error": "Reseptien nimien täytyy olla yksilöllisiä",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Resepti luotu",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "1 kuppi jauhoja",
|
"ingredient-note": "1 kupillinen jauhoja",
|
||||||
"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"
|
"step-text": "Reseptin vaiheet sekä muut kentät reseptisivulla tukevat markdown-syntaksia.\n\n**Lisää linkki**\n\n[Oma linkki](https://demo.mealie.io)\n"
|
||||||
},
|
},
|
||||||
"servings-text": {
|
"servings-text": {
|
||||||
"makes": "Makes",
|
"makes": "Tekee",
|
||||||
"serves": "Serves",
|
"serves": "Annoksia",
|
||||||
"serving": "Serving",
|
"serving": "Annos",
|
||||||
"servings": "Servings",
|
"servings": "Annosta",
|
||||||
"yield": "Yield",
|
"yield": "Tuotto",
|
||||||
"yields": "Yields"
|
"yields": "Tuottaa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mealplan": {
|
"mealplan": {
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"user-updated": "Käyttäjä päivitetty",
|
"user-updated": "Käyttäjä päivitetty",
|
||||||
"password-updated": "Salasana päivitetty",
|
"password-updated": "Salasana päivitetty",
|
||||||
"invalid-current-password": "Nykyinen salasana ei kelpaa",
|
"invalid-current-password": "Nykyinen salasana ei kelpaa",
|
||||||
"ldap-update-password-unavailable": "Salasanaa ei voitu päivittää. Käyttäjä hallinnoi LDAP:ia."
|
"ldap-update-password-unavailable": "Salasanaa ei voitu päivittää, sillä käyttäjä on LDAP-hakemiston alainen"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"report-deleted": "Raportti poistettu."
|
"report-deleted": "Raportti poistettu."
|
||||||
@@ -56,25 +56,25 @@
|
|||||||
},
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"password": {
|
"password": {
|
||||||
"subject": "Mealie Forgot Password",
|
"subject": "Mealie Unohtunut salasana",
|
||||||
"header_text": "Forgot Password",
|
"header_text": "Unohdin salasanan",
|
||||||
"message_top": "You have requested to reset your password.",
|
"message_top": "Olet pyytänyt salasanasi nollausta.",
|
||||||
"message_bottom": "Please click the button above to reset your password.",
|
"message_bottom": "Klikkaa yllä olevaa painiketta nollataksesi salasanasi.",
|
||||||
"button_text": "Reset Password"
|
"button_text": "Nollaa salasana"
|
||||||
},
|
},
|
||||||
"invitation": {
|
"invitation": {
|
||||||
"subject": "Invitation to join Mealie",
|
"subject": "Kutsu liittyä Mealieen",
|
||||||
"header_text": "You're Invited!",
|
"header_text": "Sinut on kutsuttu.",
|
||||||
"message_top": "You have been invited to join Mealie.",
|
"message_top": "Sinut on kutsuttu liittymään Mealieen.",
|
||||||
"message_bottom": "Please click the button above to accept the invitation.",
|
"message_bottom": "Ole hyvä ja klikkaa yllä olevaa painiketta hyväksyäksesi kutsun.",
|
||||||
"button_text": "Accept Invitation"
|
"button_text": "Hyväksy kutsu"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"subject": "Mealie Test Email",
|
"subject": "Mealie Testiviesti",
|
||||||
"header_text": "Test Email",
|
"header_text": "Testiviesti",
|
||||||
"message_top": "This is a test email.",
|
"message_top": "Tämä on testiviesti.",
|
||||||
"message_bottom": "Please click the button above to test the email.",
|
"message_bottom": "Ole hyvä ja klikkaa yllä olevaa painiketta testataksesi sähköpostia.",
|
||||||
"button_text": "Open Mealie"
|
"button_text": "Avaa Mealie"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Les noms de recette doivent être uniques",
|
"unique-name-error": "Les noms de recette doivent être uniques",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Recette créée",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "100 g de farine",
|
"ingredient-note": "100 g de farine",
|
||||||
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
||||||
},
|
},
|
||||||
"servings-text": {
|
"servings-text": {
|
||||||
"makes": "Makes",
|
"makes": "Fait",
|
||||||
"serves": "Serves",
|
"serves": "Pour",
|
||||||
"serving": "Serving",
|
"serving": "Portion",
|
||||||
"servings": "Servings",
|
"servings": "Portions",
|
||||||
"yield": "Yield",
|
"yield": "Quantité",
|
||||||
"yields": "Yields"
|
"yields": "Produit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mealplan": {
|
"mealplan": {
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Les noms de recette doivent être uniques",
|
"unique-name-error": "Les noms de recette doivent être uniques",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Recette créée",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "1 tasse de Farine",
|
"ingredient-note": "1 tasse de Farine",
|
||||||
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
"step-text": "Les étapes de la recette ainsi que les autres champs de la page de recette supportent la syntaxe markdown.\n\n**Ajouter un lien**\n\n[Mon lien](https://demo.mealie.io)\n"
|
||||||
},
|
},
|
||||||
"servings-text": {
|
"servings-text": {
|
||||||
"makes": "Makes",
|
"makes": "Fait",
|
||||||
"serves": "Serves",
|
"serves": "Sert",
|
||||||
"serving": "Serving",
|
"serving": "Portion",
|
||||||
"servings": "Servings",
|
"servings": "Portions",
|
||||||
"yield": "Yield",
|
"yield": "Rendement",
|
||||||
"yields": "Yields"
|
"yields": "Rendements"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mealplan": {
|
"mealplan": {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"generic": {
|
"generic": {
|
||||||
"server-error": "An unexpected error occurred"
|
"server-error": "Ocorreu un erro inesperado"
|
||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Recipe names must be unique",
|
"unique-name-error": "Os nomes de receitas deven ser únicos",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Receita creada",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "1 Cup Flour",
|
"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"
|
"step-text": "Os pasos da receita, como outros campos na páxina da receita, suportan a sintaxe markdown.\n\n**Adicionar un link**\n\n[Meu link](https://demo.mealie.io)\n"
|
||||||
},
|
},
|
||||||
"servings-text": {
|
"servings-text": {
|
||||||
"makes": "Makes",
|
"makes": "Makes",
|
||||||
@@ -33,26 +33,26 @@
|
|||||||
"exceptions": {
|
"exceptions": {
|
||||||
"permission_denied": "You do not have permission to perform this action",
|
"permission_denied": "You do not have permission to perform this action",
|
||||||
"no-entry-found": "The requested resource was not found",
|
"no-entry-found": "The requested resource was not found",
|
||||||
"integrity-error": "Database integrity error",
|
"integrity-error": "Erro de integridade da base de datos",
|
||||||
"username-conflict-error": "This username is already taken",
|
"username-conflict-error": "Este nome de usuario xa está en uso",
|
||||||
"email-conflict-error": "This email is already in use"
|
"email-conflict-error": "This email is already in use"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"generic-created": "{name} was created",
|
"generic-created": "{name} creado",
|
||||||
"generic-updated": "{name} was updated",
|
"generic-updated": "{name} atualizado",
|
||||||
"generic-created-with-url": "{name} has been created, {url}",
|
"generic-created-with-url": "{name} foi creado, {url}",
|
||||||
"generic-updated-with-url": "{name} has been updated, {url}",
|
"generic-updated-with-url": "{name} foi atualizado, {url}",
|
||||||
"generic-duplicated": "{name} has been duplicated",
|
"generic-duplicated": "{name} foi duplicado",
|
||||||
"generic-deleted": "{name} has been deleted"
|
"generic-deleted": "{name} has been deleted"
|
||||||
},
|
},
|
||||||
"datetime": {
|
"datetime": {
|
||||||
"year": "year|years",
|
"year": "ano|anos",
|
||||||
"day": "day|days",
|
"day": "dia|dias",
|
||||||
"hour": "hour|hours",
|
"hour": "hora|horas",
|
||||||
"minute": "minute|minutes",
|
"minute": "minuto|minutos",
|
||||||
"second": "second|seconds",
|
"second": "segundo|segundos",
|
||||||
"millisecond": "millisecond|milliseconds",
|
"millisecond": "milisegundo|milisegundos",
|
||||||
"microsecond": "microsecond|microseconds"
|
"microsecond": "microsegundo|microsegundos"
|
||||||
},
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"password": {
|
"password": {
|
||||||
@@ -67,14 +67,14 @@
|
|||||||
"header_text": "You're Invited!",
|
"header_text": "You're Invited!",
|
||||||
"message_top": "You have been invited to join Mealie.",
|
"message_top": "You have been invited to join Mealie.",
|
||||||
"message_bottom": "Please click the button above to accept the invitation.",
|
"message_bottom": "Please click the button above to accept the invitation.",
|
||||||
"button_text": "Accept Invitation"
|
"button_text": "Aceptar Convite"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"subject": "Mealie Test Email",
|
"subject": "Mealie Test Email",
|
||||||
"header_text": "Test Email",
|
"header_text": "Test Email",
|
||||||
"message_top": "This is a test email.",
|
"message_top": "This is a test email.",
|
||||||
"message_bottom": "Please click the button above to test the email.",
|
"message_bottom": "Please click the button above to test the email.",
|
||||||
"button_text": "Open Mealie"
|
"button_text": "Abrir o Mealie"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Receptų pavadinimai turi būti unikalūs",
|
"unique-name-error": "Receptų pavadinimai turi būti unikalūs",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Receptas sukurtas",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "1 Cup Flour",
|
"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"
|
"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"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"makes": "Makes",
|
"makes": "Makes",
|
||||||
"serves": "Serves",
|
"serves": "Serves",
|
||||||
"serving": "Serving",
|
"serving": "Serving",
|
||||||
"servings": "Servings",
|
"servings": "Porcijos",
|
||||||
"yield": "Yield",
|
"yield": "Yield",
|
||||||
"yields": "Yields"
|
"yields": "Yields"
|
||||||
}
|
}
|
||||||
@@ -46,13 +46,13 @@
|
|||||||
"generic-deleted": "{name} ištrintas"
|
"generic-deleted": "{name} ištrintas"
|
||||||
},
|
},
|
||||||
"datetime": {
|
"datetime": {
|
||||||
"year": "year|years",
|
"year": "metai|metai",
|
||||||
"day": "day|days",
|
"day": "diena|dienos",
|
||||||
"hour": "hour|hours",
|
"hour": "valanda|valandos",
|
||||||
"minute": "minute|minutes",
|
"minute": "minutė|minutės",
|
||||||
"second": "second|seconds",
|
"second": "sekundė|sekundės",
|
||||||
"millisecond": "millisecond|milliseconds",
|
"millisecond": "milisekundė|milisekundės",
|
||||||
"microsecond": "microsecond|microseconds"
|
"microsecond": "mikrosekundė|mikrosekundės"
|
||||||
},
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"password": {
|
"password": {
|
||||||
@@ -67,12 +67,12 @@
|
|||||||
"header_text": "You're Invited!",
|
"header_text": "You're Invited!",
|
||||||
"message_top": "You have been invited to join Mealie.",
|
"message_top": "You have been invited to join Mealie.",
|
||||||
"message_bottom": "Please click the button above to accept the invitation.",
|
"message_bottom": "Please click the button above to accept the invitation.",
|
||||||
"button_text": "Accept Invitation"
|
"button_text": "Priimti pakvietimą"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"subject": "Mealie Test Email",
|
"subject": "Mealie Test Email",
|
||||||
"header_text": "Test Email",
|
"header_text": "Test Email",
|
||||||
"message_top": "This is a test email.",
|
"message_top": "Tai bandomasis el. laiškas.",
|
||||||
"message_bottom": "Please click the button above to test the email.",
|
"message_bottom": "Please click the button above to test the email.",
|
||||||
"button_text": "Open Mealie"
|
"button_text": "Open Mealie"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
},
|
},
|
||||||
"recipe": {
|
"recipe": {
|
||||||
"unique-name-error": "Nomes de receitas devem ser únicos",
|
"unique-name-error": "Nomes de receitas devem ser únicos",
|
||||||
"recipe-created": "Recipe Created",
|
"recipe-created": "Receita criada",
|
||||||
"recipe-defaults": {
|
"recipe-defaults": {
|
||||||
"ingredient-note": "1 Xícara de Farinha",
|
"ingredient-note": "1 Xícara de Farinha",
|
||||||
"step-text": "Passos das receitas, assim como outros campos na página da receita, suportam sintaxe \"markdown\".\n\n**Adicione um link**\n\n[Meu Link](https://demo.mealie.io)\n"
|
"step-text": "Passos das receitas, assim como outros campos na página da receita, suportam sintaxe \"markdown\".\n\n**Adicione um link**\n\n[Meu Link](https://demo.mealie.io)\n"
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
"servings-text": {
|
"servings-text": {
|
||||||
"makes": "Makes",
|
"makes": "Makes",
|
||||||
"serves": "Serves",
|
"serves": "Serves",
|
||||||
"serving": "Serving",
|
"serving": "Porção",
|
||||||
"servings": "Servings",
|
"servings": "Porções",
|
||||||
"yield": "Yield",
|
"yield": "Yield",
|
||||||
"yields": "Yields"
|
"yields": "Yields"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user