mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-26 11:49:41 -05:00
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
163c2a53b6 | ||
|
|
aba45657c3 | ||
|
|
6cedde7b2d | ||
|
|
44baa8322c | ||
|
|
0fbb95438a | ||
|
|
c56dd9563c | ||
|
|
0008b7c975 | ||
|
|
524f086cc5 | ||
|
|
8550387e0c | ||
|
|
1618f8df79 | ||
|
|
22dfb2a410 | ||
|
|
6973c65142 | ||
|
|
a01f86a14e | ||
|
|
9704268fdc | ||
|
|
84cc4c1165 | ||
|
|
5cb70becb8 | ||
|
|
5f99abf459 | ||
|
|
4a8ddce391 | ||
|
|
9a14a87c27 | ||
|
|
c01634f9bd | ||
|
|
f055df3b4d | ||
|
|
a83f474d70 | ||
|
|
63d358df36 | ||
|
|
e70548fcc0 | ||
|
|
17b03905e6 | ||
|
|
90403e6a13 | ||
|
|
db400cae25 | ||
|
|
0f8eee4e0f | ||
|
|
1f532f6276 | ||
|
|
b32715e493 | ||
|
|
0d19e12118 | ||
|
|
96e5213fa6 | ||
|
|
44c567d20b | ||
|
|
3c920593cf | ||
|
|
1d90f8b6f1 | ||
|
|
6b1217ec35 | ||
|
|
a71564a424 | ||
|
|
76c2e144fc | ||
|
|
981353380c | ||
|
|
96a520b1af | ||
|
|
05f537dc6b | ||
|
|
948d8da3b1 | ||
|
|
f8e4b39d88 | ||
|
|
6c498f7dac | ||
|
|
d25702b717 | ||
|
|
aca18fcbe0 | ||
|
|
98b57d2854 | ||
|
|
5e1c804fd1 | ||
|
|
a30deb4bae | ||
|
|
45a567856a | ||
|
|
7065d96f90 | ||
|
|
f8cd42dec9 | ||
|
|
8d736c0f88 | ||
|
|
8183e350c9 | ||
|
|
4438bfcb89 | ||
|
|
f42b2cfd31 | ||
|
|
09131e8eae | ||
|
|
f5f001b3d2 | ||
|
|
7f8587922d | ||
|
|
a3460bc023 | ||
|
|
5faa74a75d | ||
|
|
65dbc643d3 | ||
|
|
f0b169647b | ||
|
|
d786ee09fa | ||
|
|
a46f3958fe | ||
|
|
6c17937313 | ||
|
|
a26835ccc4 | ||
|
|
86fc4aa2d0 | ||
|
|
4bd3da451d | ||
|
|
0003405e98 | ||
|
|
b586794337 | ||
|
|
460cb43113 | ||
|
|
5128fcc9eb | ||
|
|
243ff8601c | ||
|
|
97f8d46afb | ||
|
|
e469ebf35e | ||
|
|
e04c729476 | ||
|
|
d98bf9155d | ||
|
|
e98d00a962 | ||
|
|
cf5f896cec | ||
|
|
e8d616ac98 | ||
|
|
7a22d43959 | ||
|
|
6b68f48227 | ||
|
|
115f18889a | ||
|
|
0aaffb7545 | ||
|
|
087cbdade8 | ||
|
|
7e55115a3a | ||
|
|
31ee55a113 | ||
|
|
61be55e4b7 | ||
|
|
e3f695bde1 | ||
|
|
0fb3d22f6a | ||
|
|
7ba5187ecf | ||
|
|
168c0f3a0d | ||
|
|
1179e226ab | ||
|
|
bed22c055d | ||
|
|
c25a1df480 | ||
|
|
d1df772218 | ||
|
|
cbdd23020b | ||
|
|
10f8a56343 | ||
|
|
006c5b3af8 | ||
|
|
562a0dceae | ||
|
|
cde03a0f33 | ||
|
|
b42285a9a5 | ||
|
|
f4d4a5b714 | ||
|
|
ee7d611086 | ||
|
|
e51fda5f20 | ||
|
|
bee759e166 | ||
|
|
5802dfd0a5 | ||
|
|
c18ce7635d | ||
|
|
942a8a6119 | ||
|
|
4015edde90 | ||
|
|
1c32940f5c | ||
|
|
447ffa9fe2 | ||
|
|
8480234592 | ||
|
|
2e0345a4a8 | ||
|
|
49fc0cf80f | ||
|
|
c67ecb6e31 | ||
|
|
b4f12c4e84 | ||
|
|
0b2adf5249 | ||
|
|
7dcb5884d9 | ||
|
|
35bd550101 | ||
|
|
707abfacb0 | ||
|
|
ed4f4c77e8 | ||
|
|
c492fb513b | ||
|
|
310b8e04e1 | ||
|
|
efeae4debc | ||
|
|
6bc25c32ff | ||
|
|
7f2b0438fe | ||
|
|
8481f8c658 | ||
|
|
d842795c25 | ||
|
|
58dd700207 | ||
|
|
1331d2cb6d | ||
|
|
ad2a613fd8 | ||
|
|
0565189580 | ||
|
|
5aa351b885 | ||
|
|
d5226eb5cf | ||
|
|
9ead1d0022 | ||
|
|
67342c3ba9 | ||
|
|
3fecd82cd0 | ||
|
|
a033c4290f | ||
|
|
b6597af0d7 | ||
|
|
af6ed4bd24 | ||
|
|
cc4bddb3fe | ||
|
|
95a9df9c05 | ||
|
|
c44de28c2c | ||
|
|
9f1b87fa4f | ||
|
|
b96e0bab11 | ||
|
|
fe97fb371b | ||
|
|
bb7df960cc | ||
|
|
c3c7d803dc | ||
|
|
99ce3327cc | ||
|
|
d1949df23d | ||
|
|
119b47c3c4 | ||
|
|
8b50c59ad3 | ||
|
|
e2ac65467b | ||
|
|
c5cc492f0a | ||
|
|
8c73b5254c | ||
|
|
4b0315ffd3 | ||
|
|
6a96f5b7c5 | ||
|
|
8875dd4083 | ||
|
|
7299f265d3 | ||
|
|
cac186f63c | ||
|
|
fd4c571e48 | ||
|
|
db99450475 | ||
|
|
ec50add571 | ||
|
|
1fc3746619 | ||
|
|
debf4c124a | ||
|
|
0e071255e5 | ||
|
|
4d0b8c690b | ||
|
|
85b3e0a0a6 | ||
|
|
d8573ce16f | ||
|
|
7f7e3180fa | ||
|
|
57cc6feef0 | ||
|
|
fb198a80d2 | ||
|
|
5b324a86dc | ||
|
|
f633274bef | ||
|
|
65b2eb6d7e | ||
|
|
0c509ec02e | ||
|
|
babcddeeb1 | ||
|
|
f23b282f27 | ||
|
|
fcfedd3026 | ||
|
|
efd65c1024 | ||
|
|
6f4f5381ff | ||
|
|
95b63f5180 | ||
|
|
f33a52a94c | ||
|
|
90baf26eb8 |
@@ -1,12 +1,7 @@
|
||||
FROM python:3.10-alpine3.18
|
||||
FROM python:3.13-alpine3.22
|
||||
|
||||
#Install all dependencies.
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git yarn
|
||||
|
||||
# Fix libxml error from xmlsec https://github.com/xmlsec/python-xmlsec/issues/257#issuecomment-1738620862
|
||||
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.15/community/" | tee -a /etc/apk/repositories
|
||||
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.15/main" | tee -a /etc/apk/repositories
|
||||
RUN apk add --no-cache libxml2-dev=2.9.14-r2 xmlsec-dev=1.2.33-r0
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git yarn libgcc libstdc++ nginx tini envsubst nodejs npm
|
||||
|
||||
#Print all logs without buffering it.
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
@@ -24,8 +19,10 @@ RUN \
|
||||
if [ `apk --print-arch` = "armv7" ]; then \
|
||||
printf "[global]\nextra-index-url=https://www.piwheels.org/simple\n" > /etc/pip.conf ; \
|
||||
fi
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev && \
|
||||
echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \
|
||||
pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt && \
|
||||
rm -rf /tmp/pip-tmp && \
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl rust && \
|
||||
python -m pip install --upgrade pip && \
|
||||
pip debug -v && \
|
||||
pip install wheel==0.45.1 && \
|
||||
pip install setuptools_rust==1.10.2 && \
|
||||
pip install -r /tmp/pip-tmp/requirements.txt --no-cache-dir &&\
|
||||
apk --purge del .build-deps
|
||||
4
.github/workflows/build-docker.yml
vendored
4
.github/workflows/build-docker.yml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
- name: Discord notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.3.2
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: '🚀 Version {{ VERSION }} of tandoor has been released 🥳 Check it out https://github.com/vabene1111/recipes/releases/tag/{{ VERSION }}'
|
||||
|
||||
@@ -121,6 +121,6 @@ jobs:
|
||||
- name: Discord notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_BETA_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.3.2
|
||||
uses: Ilshidur/action-discord@0.4.0
|
||||
with:
|
||||
args: '🚀 The Tandoor 2 Image has been updated! 🥳'
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
node-version: ["22"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: awalsh128/cache-apt-pkgs-action@v1.4.3
|
||||
- uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libsasl2-dev python3-dev libxml2-dev libxmlsec1-dev libxslt-dev libxmlsec1-openssl libxslt-dev libldap2-dev libssl-dev gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl
|
||||
version: 1.0
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -91,3 +91,4 @@ cookbook/static/vue3
|
||||
vue3/node_modules
|
||||
cookbook/tests/other/docs/reports/tests/tests.html
|
||||
cookbook/tests/other/docs/reports/tests/pytest.xml
|
||||
vue3/src/plugins
|
||||
|
||||
62
.vscode/tasks.json
vendored
62
.vscode/tasks.json
vendored
@@ -14,28 +14,16 @@
|
||||
},
|
||||
{
|
||||
"label": "Setup Dev Server",
|
||||
"dependsOn": ["Run Migrations", "Yarn Build"]
|
||||
"dependsOn": ["Run Migrations"]
|
||||
},
|
||||
{
|
||||
"label": "Run Dev Server",
|
||||
"type": "shell",
|
||||
"type": "shell",
|
||||
"dependsOn": ["Setup Dev Server"],
|
||||
"command": "python3 manage.py runserver"
|
||||
"command": "DEBUG=1 python3 manage.py runserver"
|
||||
},
|
||||
{
|
||||
"label": "Yarn Install",
|
||||
"dependsOn": ["Yarn Install - Vue", "Yarn Install - Vue3"]
|
||||
},
|
||||
{
|
||||
"label": "Yarn Install - Vue",
|
||||
"type": "shell",
|
||||
"command": "yarn install --force",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Yarn Install - Vue3",
|
||||
"type": "shell",
|
||||
"command": "yarn install --force",
|
||||
"options": {
|
||||
@@ -44,18 +32,6 @@
|
||||
},
|
||||
{
|
||||
"label": "Generate API",
|
||||
"dependsOn": ["Generate API - Vue", "Generate API - Vue3"]
|
||||
},
|
||||
{
|
||||
"label": "Generate API - Vue",
|
||||
"type": "shell",
|
||||
"command": "openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue/src/utils/openapi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Generate API - Vue3",
|
||||
"type": "shell",
|
||||
"command": "openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/",
|
||||
"options": {
|
||||
@@ -63,43 +39,19 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Yarn Serve",
|
||||
"label": "Yarn Dev",
|
||||
"type": "shell",
|
||||
"command": "yarn serve",
|
||||
"dependsOn": ["Yarn Install - Vue"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Vite Serve",
|
||||
"type": "shell",
|
||||
"command": "vite",
|
||||
"dependsOn": ["Yarn Install - Vue3"],
|
||||
"command": "yarn dev",
|
||||
"dependsOn": ["Yarn Install"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Yarn Build",
|
||||
"dependsOn": ["Yarn Build - Vue", "Vite Build - Vue3"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "Yarn Build - Vue",
|
||||
"type": "shell",
|
||||
"command": "yarn build",
|
||||
"dependsOn": ["Yarn Install - Vue"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "Vite Build - Vue3",
|
||||
"type": "shell",
|
||||
"command": "vite build",
|
||||
"dependsOn": ["Yarn Install - Vue3"],
|
||||
"dependsOn": ["Yarn Install"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/vue3"
|
||||
},
|
||||
|
||||
29
Dockerfile
29
Dockerfile
@@ -1,12 +1,11 @@
|
||||
FROM python:3.13-alpine3.21
|
||||
FROM python:3.13-alpine3.22
|
||||
|
||||
#Install all dependencies.
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx
|
||||
RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx tini envsubst nodejs npm
|
||||
|
||||
#Print all logs without buffering it.
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
ENV DOCKER true
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
DOCKER=true
|
||||
|
||||
#This port will be used by gunicorn.
|
||||
EXPOSE 80 8080
|
||||
@@ -17,23 +16,14 @@ WORKDIR /opt/recipes
|
||||
|
||||
COPY requirements.txt ./
|
||||
|
||||
RUN \
|
||||
if [ `apk --print-arch` = "armv7" ]; then \
|
||||
printf "[global]\nextra-index-url=https://www.piwheels.org/simple\n" > /etc/pip.conf ; \
|
||||
fi
|
||||
|
||||
# remove Development dependencies from requirements.txt
|
||||
RUN sed -i '/# Development/,$d' requirements.txt
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl && \
|
||||
echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-dev jpeg-dev libwebp-dev openssl-dev libffi-dev cargo openldap-dev python3-dev xmlsec-dev xmlsec build-base g++ curl rust && \
|
||||
python -m venv venv && \
|
||||
/opt/recipes/venv/bin/python -m pip install --upgrade pip && \
|
||||
venv/bin/pip debug -v && \
|
||||
venv/bin/pip install wheel==0.45.1 && \
|
||||
venv/bin/pip install setuptools_rust==1.10.2 && \
|
||||
if [ `apk --print-arch` = "aarch64" ]; then \
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y; \
|
||||
fi &&\
|
||||
venv/bin/pip install -r requirements.txt --no-cache-dir &&\
|
||||
apk --purge del .build-deps
|
||||
|
||||
@@ -41,8 +31,11 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de
|
||||
COPY . ./
|
||||
|
||||
# delete default nginx config and link it to tandoors config
|
||||
RUN rm -rf /etc/nginx/http.d
|
||||
RUN ln -s /opt/recipes/http.d /etc/nginx/http.d
|
||||
# create symlinks to access and error log to show them on stdout
|
||||
RUN rm -rf /etc/nginx/http.d && \
|
||||
ln -s /opt/recipes/http.d /etc/nginx/http.d && \
|
||||
ln -sf /dev/stdout /var/log/nginx/access.log && \
|
||||
ln -sf /dev/stderr /var/log/nginx/error.log
|
||||
|
||||
# commented for now https://github.com/TandoorRecipes/recipes/issues/3478
|
||||
#HEALTHCHECK --interval=30s \
|
||||
@@ -57,4 +50,4 @@ RUN /opt/recipes/venv/bin/python version.py
|
||||
RUN find . -type d -name ".git" | xargs rm -rf
|
||||
|
||||
RUN chmod +x boot.sh
|
||||
ENTRYPOINT ["/opt/recipes/boot.sh"]
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/opt/recipes/boot.sh"]
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<a href="https://docs.tandoor.dev/install/docker/" target="_blank" rel="noopener noreferrer">Installation</a> •
|
||||
<a href="https://docs.tandoor.dev/" target="_blank" rel="noopener noreferrer">Docs</a> •
|
||||
<a href="https://app.tandoor.dev/accounts/login/?demo" target="_blank" rel="noopener noreferrer">Demo</a> •
|
||||
<a href="https://community.tandoor.dev" target="_blank" rel="noopener noreferrer">Community</a> •
|
||||
<a href="https://discord.gg/RhzBrfWgtp" target="_blank" rel="noopener noreferrer">Discord</a>
|
||||
</p>
|
||||
|
||||
@@ -81,13 +82,13 @@ Share some information on how you use Tandoor to help me improve the application
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://discord.gg/RhzBrfWgtp">Discord</a></td>
|
||||
<td>We have a public Discord server that anyone can join. This is where all our developers and contributors hang out and where we make announcements</td>
|
||||
<td><a href="https://community.tandoor.dev">Community</a></td>
|
||||
<td>Get support, share best practices, discuss feature ideas, and meet other Tandoor users.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="https://twitter.com/TandoorRecipes">Twitter</a></td>
|
||||
<td>You can follow our Twitter account to get updates on new features or releases</td>
|
||||
<td><a href="https://discord.gg/RhzBrfWgtp">Discord</a></td>
|
||||
<td>We have a public Discord server that anyone can join. This is where all our developers and contributors hang out and where we make announcements</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
19
boot.sh
Normal file → Executable file
19
boot.sh
Normal file → Executable file
@@ -1,11 +1,17 @@
|
||||
#!/bin/sh
|
||||
source venv/bin/activate
|
||||
|
||||
TANDOOR_PORT="${TANDOOR_PORT:-8080}"
|
||||
# these are envsubst in the nginx config, make sure they default to something sensible when unset
|
||||
export TANDOOR_PORT="${TANDOOR_PORT:-8080}"
|
||||
export MEDIA_ROOT=${MEDIA_ROOT:-/opt/recipes/mediafiles};
|
||||
export STATIC_ROOT=${STATIC_ROOT:-/opt/recipes/staticfiles};
|
||||
|
||||
GUNICORN_WORKERS="${GUNICORN_WORKERS:-3}"
|
||||
GUNICORN_THREADS="${GUNICORN_THREADS:-2}"
|
||||
GUNICORN_LOG_LEVEL="${GUNICORN_LOG_LEVEL:-'info'}"
|
||||
|
||||
PLUGINS_BUILD="${PLUGINS_BUILD:-0}"
|
||||
|
||||
if [ "${TANDOOR_PORT}" -eq 80 ]; then
|
||||
echo "TANDOOR_PORT set to 8080 because 80 is now taken by the integrated nginx"
|
||||
TANDOOR_PORT=8080
|
||||
@@ -78,19 +84,26 @@ echo "Database is ready"
|
||||
|
||||
echo "Migrating database"
|
||||
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
if [ "${PLUGINS_BUILD}" -eq 1 ]; then
|
||||
echo "Running yarn build at startup because PLUGINS_BUILD is enabled"
|
||||
python plugin.py
|
||||
fi
|
||||
|
||||
echo "Collecting static files, this may take a while..."
|
||||
|
||||
python manage.py collectstatic --noinput
|
||||
|
||||
echo "Done"
|
||||
|
||||
chmod -R 755 /opt/recipes/mediafiles
|
||||
chmod -R 755 ${MEDIA_ROOT:-/opt/recipes/mediafiles}
|
||||
|
||||
ipv6_disable=$(cat /sys/module/ipv6/parameters/disable)
|
||||
|
||||
# prepare nginx config
|
||||
envsubst '$MEDIA_ROOT $STATIC_ROOT $TANDOOR_PORT' < /opt/recipes/http.d/Recipes.conf.template > /opt/recipes/http.d/Recipes.conf
|
||||
|
||||
# start nginx
|
||||
echo "Starting nginx"
|
||||
nginx
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_filetype(name):
|
||||
|
||||
def is_file_type_allowed(filename, image_only=False):
|
||||
is_file_allowed = False
|
||||
allowed_file_types = ['.pdf', '.docx', '.xlsx', '.css']
|
||||
allowed_file_types = ['.pdf', '.docx', '.xlsx', '.css', '.mp4', '.mov']
|
||||
allowed_image_types = ['.png', '.jpg', '.jpeg', '.gif', '.webp']
|
||||
check_list = allowed_image_types
|
||||
if not image_only:
|
||||
@@ -77,6 +77,8 @@ def handle_image(request, image_object, filetype):
|
||||
file_format = 'JPEG'
|
||||
if filetype == '.png':
|
||||
file_format = 'PNG'
|
||||
if filetype == '.webp':
|
||||
file_format = 'WEBP'
|
||||
|
||||
if (image_object.size / 1000) > 500: # if larger than 500 kb compress
|
||||
if filetype == '.jpeg' or filetype == '.jpg':
|
||||
|
||||
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||
"PO-Revision-Date: 2025-02-16 14:58+0000\n"
|
||||
"Last-Translator: Elvis Gosselin <elvis.gosselin@tutanota.com>\n"
|
||||
"PO-Revision-Date: 2025-08-10 11:36+0000\n"
|
||||
"Last-Translator: Enzo La Rafale <enzo.chaussivert@gmail.com>\n"
|
||||
"Language-Team: French <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/fr/>\n"
|
||||
"Language: fr\n"
|
||||
@@ -405,7 +405,7 @@ msgstr "Rubrique"
|
||||
|
||||
#: .\cookbook\management\commands\fix_duplicate_properties.py:15
|
||||
msgid "Fixes foods with "
|
||||
msgstr ""
|
||||
msgstr "Corriger les aliments avec "
|
||||
|
||||
#: .\cookbook\management\commands\rebuildindex.py:14
|
||||
msgid "Rebuilds full text search index on Recipe"
|
||||
@@ -442,8 +442,6 @@ msgid "Other"
|
||||
msgstr "Autre"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:17
|
||||
#, fuzzy
|
||||
#| msgid "Fats"
|
||||
msgid "Fat"
|
||||
msgstr "Matières grasses"
|
||||
|
||||
@@ -451,7 +449,7 @@ msgstr "Matières grasses"
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:18
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:19
|
||||
msgid "g"
|
||||
msgstr ""
|
||||
msgstr "g"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:18
|
||||
msgid "Carbohydrates"
|
||||
@@ -467,7 +465,7 @@ msgstr "Calories"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:20
|
||||
msgid "kcal"
|
||||
msgstr ""
|
||||
msgstr "kcal"
|
||||
|
||||
#: .\cookbook\models.py:325
|
||||
msgid ""
|
||||
@@ -554,30 +552,24 @@ msgid "Instruction Replace"
|
||||
msgstr "Remplacer l'instruction"
|
||||
|
||||
#: .\cookbook\models.py:1472
|
||||
#, fuzzy
|
||||
#| msgid "New Unit"
|
||||
msgid "Never Unit"
|
||||
msgstr "Nouvelle unité"
|
||||
msgstr "Aucune unité"
|
||||
|
||||
#: .\cookbook\models.py:1473
|
||||
msgid "Transpose Words"
|
||||
msgstr ""
|
||||
msgstr "Transposer les mots"
|
||||
|
||||
#: .\cookbook\models.py:1474
|
||||
#, fuzzy
|
||||
#| msgid "Food Alias"
|
||||
msgid "Food Replace"
|
||||
msgstr "Aliment équivalent"
|
||||
msgstr "Aliment alternatif"
|
||||
|
||||
#: .\cookbook\models.py:1475
|
||||
#, fuzzy
|
||||
#| msgid "Description Replace"
|
||||
msgid "Unit Replace"
|
||||
msgstr "Remplacer la Description"
|
||||
msgstr "Remplacer l'unité"
|
||||
|
||||
#: .\cookbook\models.py:1476
|
||||
msgid "Name Replace"
|
||||
msgstr ""
|
||||
msgstr "Remplacer le nom"
|
||||
|
||||
#: .\cookbook\models.py:1503 .\cookbook\views\delete.py:40
|
||||
#: .\cookbook\views\edit.py:210 .\cookbook\views\new.py:39
|
||||
@@ -1040,10 +1032,8 @@ msgid "Properties"
|
||||
msgstr "Propriétés"
|
||||
|
||||
#: .\cookbook\templates\base.html:301 .\cookbook\views\lists.py:255
|
||||
#, fuzzy
|
||||
#| msgid "Account Connections"
|
||||
msgid "Unit Conversions"
|
||||
msgstr "Comptes connectés"
|
||||
msgstr "Conversions d'unités"
|
||||
|
||||
#: .\cookbook\templates\base.html:318 .\cookbook\templates\index.html:47
|
||||
msgid "Import Recipe"
|
||||
@@ -1063,10 +1053,8 @@ msgid "Space Settings"
|
||||
msgstr "Paramètres de groupe"
|
||||
|
||||
#: .\cookbook\templates\base.html:340
|
||||
#, fuzzy
|
||||
#| msgid "External Recipes"
|
||||
msgid "External Connectors"
|
||||
msgstr "Recettes externes"
|
||||
msgstr "Connecteurs externes"
|
||||
|
||||
#: .\cookbook\templates\base.html:345 .\cookbook\templates\system.html:13
|
||||
msgid "System"
|
||||
@@ -1530,10 +1518,8 @@ msgid "Back"
|
||||
msgstr "Retour"
|
||||
|
||||
#: .\cookbook\templates\property_editor.html:7
|
||||
#, fuzzy
|
||||
#| msgid "Ingredient Editor"
|
||||
msgid "Property Editor"
|
||||
msgstr "Éditeur d’ingrédients"
|
||||
msgstr "Éditeur de propriété"
|
||||
|
||||
#: .\cookbook\templates\recipe_view.html:36
|
||||
msgid "Comments"
|
||||
@@ -2011,10 +1997,8 @@ msgid "Sign in using"
|
||||
msgstr "Se connecter avec"
|
||||
|
||||
#: .\cookbook\templates\space_manage.html:7
|
||||
#, fuzzy
|
||||
#| msgid "Space Membership"
|
||||
msgid "Space Management"
|
||||
msgstr "Adhésion à l'espace"
|
||||
msgstr "Gestion de l'espace"
|
||||
|
||||
#: .\cookbook\templates\space_manage.html:26
|
||||
msgid "Space:"
|
||||
@@ -2227,10 +2211,8 @@ msgid "Info"
|
||||
msgstr "Info"
|
||||
|
||||
#: .\cookbook\templates\system.html:110 .\cookbook\templates\system.html:127
|
||||
#, fuzzy
|
||||
#| msgid "Use fractions"
|
||||
msgid "Migrations"
|
||||
msgstr "Utiliser les fractions"
|
||||
msgstr "Migrations"
|
||||
|
||||
#: .\cookbook\templates\system.html:116
|
||||
msgid ""
|
||||
@@ -2265,10 +2247,8 @@ msgid "Hide"
|
||||
msgstr "Cacher"
|
||||
|
||||
#: .\cookbook\templates\system.html:210
|
||||
#, fuzzy
|
||||
#| msgid "Show Log"
|
||||
msgid "Show"
|
||||
msgstr "Afficher le journal"
|
||||
msgstr "Afficher"
|
||||
|
||||
#: .\cookbook\templates\url_import.html:8
|
||||
msgid "URL Import"
|
||||
@@ -2353,11 +2333,9 @@ msgstr ""
|
||||
"MM-DD."
|
||||
|
||||
#: .\cookbook\views\api.py:744
|
||||
#, fuzzy
|
||||
#| msgid "ID of recipe a step is part of. For multiple repeat parameter."
|
||||
msgid "Filter meal plans with MealType ID. For multiple repeat parameter."
|
||||
msgstr ""
|
||||
"Identifiant de la recette dont fait partie une étape. Pour plusieurs "
|
||||
"Filtrer le planning des repas avec l'identifiant MealType. Pour plusieurs "
|
||||
"paramètres de répétition."
|
||||
|
||||
#: .\cookbook\views\api.py:872
|
||||
@@ -2460,18 +2438,27 @@ msgstr ""
|
||||
#: .\cookbook\views\api.py:922
|
||||
msgid "ID of book a recipe should be in. For multiple repeat parameter."
|
||||
msgstr ""
|
||||
"ID du livre dans lequel une recette doit se trouver. Pour plusieurs "
|
||||
"paramètres de répétition."
|
||||
|
||||
#: .\cookbook\views\api.py:923
|
||||
msgid "Book IDs, repeat for multiple. Return recipes with any of the books"
|
||||
msgstr ""
|
||||
"IDs de livre, répéter pour plusieurs livres. Renvoie les recettes dans "
|
||||
"n'importe quel livre."
|
||||
|
||||
#: .\cookbook\views\api.py:924
|
||||
msgid "Book IDs, repeat for multiple. Return recipes with all of the books."
|
||||
msgstr ""
|
||||
"IDs de livre, répéter pour plusieurs livres. Renvoie les recettes dans tous "
|
||||
"les livre."
|
||||
|
||||
#: .\cookbook\views\api.py:925
|
||||
#, fuzzy
|
||||
msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books."
|
||||
msgstr ""
|
||||
"Identifiants de livres : répéter pour plusieurs. Exclure les recettes de "
|
||||
"l'un des livres."
|
||||
|
||||
#: .\cookbook\views\api.py:926
|
||||
msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books."
|
||||
|
||||
@@ -13,8 +13,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||
"PO-Revision-Date: 2025-02-16 14:58+0000\n"
|
||||
"Last-Translator: Cots Partier <cots.pastier.34@icloud.com>\n"
|
||||
"PO-Revision-Date: 2025-07-31 19:14+0000\n"
|
||||
"Last-Translator: Justin Straver <justin.straver@gmail.com>\n"
|
||||
"Language-Team: Dutch <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/nl/>\n"
|
||||
"Language: nl\n"
|
||||
@@ -46,7 +46,7 @@ msgstr "Voorbereidingstijd in minuten"
|
||||
|
||||
#: .\cookbook\forms.py:62
|
||||
msgid "Waiting time (cooking/baking) in minutes"
|
||||
msgstr "Wacht tijd in minuten (koken en bakken)"
|
||||
msgstr "Wachttijd in minuten (koken en bakken)"
|
||||
|
||||
#: .\cookbook\forms.py:63 .\cookbook\forms.py:222 .\cookbook\forms.py:246
|
||||
msgid "Path"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||
"PO-Revision-Date: 2025-07-28 17:58+0000\n"
|
||||
"PO-Revision-Date: 2025-08-01 08:40+0000\n"
|
||||
"Last-Translator: Aleksey <streltsov3@gmail.com>\n"
|
||||
"Language-Team: Russian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/ru/>\n"
|
||||
@@ -446,30 +446,32 @@ msgstr "г"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:18
|
||||
msgid "Carbohydrates"
|
||||
msgstr ""
|
||||
msgstr "Углеводы"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:19
|
||||
msgid "Proteins"
|
||||
msgstr ""
|
||||
msgstr "Протеины"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:20
|
||||
msgid "Calories"
|
||||
msgstr ""
|
||||
msgstr "Калории"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:20
|
||||
msgid "kcal"
|
||||
msgstr ""
|
||||
msgstr "ккал"
|
||||
|
||||
#: .\cookbook\models.py:325
|
||||
msgid ""
|
||||
"Maximum file storage for space in MB. 0 for unlimited, -1 to disable file "
|
||||
"upload."
|
||||
msgstr ""
|
||||
"Максимальный объем файлового хранилища в МБ. 0 — без ограничений, -1 — чтобы "
|
||||
"отключить загрузку файлов."
|
||||
|
||||
#: .\cookbook\models.py:454 .\cookbook\templates\search.html:7
|
||||
#: .\cookbook\templates\settings.html:18
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
msgstr "Поиск"
|
||||
|
||||
#: .\cookbook\models.py:455 .\cookbook\templates\base.html:114
|
||||
#: .\cookbook\templates\meal_plan.html:7
|
||||
@@ -488,27 +490,27 @@ msgstr "Покупки"
|
||||
|
||||
#: .\cookbook\models.py:752
|
||||
msgid " is part of a recipe step and cannot be deleted"
|
||||
msgstr ""
|
||||
msgstr " является частью шага рецепта и не может быть удален"
|
||||
|
||||
#: .\cookbook\models.py:918
|
||||
msgid "Nutrition"
|
||||
msgstr ""
|
||||
msgstr "Питательная ценность"
|
||||
|
||||
#: .\cookbook\models.py:918
|
||||
msgid "Allergen"
|
||||
msgstr ""
|
||||
msgstr "Аллерген"
|
||||
|
||||
#: .\cookbook\models.py:919
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
msgstr "Цена"
|
||||
|
||||
#: .\cookbook\models.py:919
|
||||
msgid "Goal"
|
||||
msgstr ""
|
||||
msgstr "Цель"
|
||||
|
||||
#: .\cookbook\models.py:1408 .\cookbook\templates\search_info.html:28
|
||||
msgid "Simple"
|
||||
msgstr ""
|
||||
msgstr "Простой"
|
||||
|
||||
#: .\cookbook\models.py:1409 .\cookbook\templates\search_info.html:33
|
||||
msgid "Phrase"
|
||||
@@ -516,19 +518,19 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\models.py:1410 .\cookbook\templates\search_info.html:38
|
||||
msgid "Web"
|
||||
msgstr ""
|
||||
msgstr "Веб"
|
||||
|
||||
#: .\cookbook\models.py:1411 .\cookbook\templates\search_info.html:47
|
||||
msgid "Raw"
|
||||
msgstr ""
|
||||
msgstr "Необработанный"
|
||||
|
||||
#: .\cookbook\models.py:1467
|
||||
msgid "Food Alias"
|
||||
msgstr ""
|
||||
msgstr "Синоним продукта"
|
||||
|
||||
#: .\cookbook\models.py:1468
|
||||
msgid "Unit Alias"
|
||||
msgstr ""
|
||||
msgstr "Синоним единицы измерения"
|
||||
|
||||
#: .\cookbook\models.py:1469
|
||||
#, fuzzy
|
||||
@@ -538,149 +540,154 @@ msgstr "Ключевые слова"
|
||||
|
||||
#: .\cookbook\models.py:1470
|
||||
msgid "Description Replace"
|
||||
msgstr ""
|
||||
msgstr "Замена описания"
|
||||
|
||||
#: .\cookbook\models.py:1471
|
||||
msgid "Instruction Replace"
|
||||
msgstr ""
|
||||
msgstr "Замена инструкции"
|
||||
|
||||
#: .\cookbook\models.py:1472
|
||||
#, fuzzy
|
||||
#| msgid "New Unit"
|
||||
msgid "Never Unit"
|
||||
msgstr "Новый юнит"
|
||||
msgstr "Без единицы"
|
||||
|
||||
#: .\cookbook\models.py:1473
|
||||
msgid "Transpose Words"
|
||||
msgstr ""
|
||||
msgstr "Транспонировать слова"
|
||||
|
||||
#: .\cookbook\models.py:1474
|
||||
msgid "Food Replace"
|
||||
msgstr ""
|
||||
msgstr "Замена продукта"
|
||||
|
||||
#: .\cookbook\models.py:1475
|
||||
msgid "Unit Replace"
|
||||
msgstr ""
|
||||
msgstr "Замена единицы измерения"
|
||||
|
||||
#: .\cookbook\models.py:1476
|
||||
msgid "Name Replace"
|
||||
msgstr ""
|
||||
msgstr "Замена названия"
|
||||
|
||||
#: .\cookbook\models.py:1503 .\cookbook\views\delete.py:40
|
||||
#: .\cookbook\views\edit.py:210 .\cookbook\views\new.py:39
|
||||
msgid "Recipe"
|
||||
msgstr ""
|
||||
msgstr "Рецепт"
|
||||
|
||||
#: .\cookbook\models.py:1504
|
||||
#, fuzzy
|
||||
#| msgid "New Food"
|
||||
msgid "Food"
|
||||
msgstr "Новый продукт"
|
||||
msgstr "Продукт"
|
||||
|
||||
#: .\cookbook\models.py:1505 .\cookbook\templates\base.html:149
|
||||
msgid "Keyword"
|
||||
msgstr ""
|
||||
msgstr "Ключевое слово"
|
||||
|
||||
#: .\cookbook\serializer.py:222
|
||||
msgid "File uploads are not enabled for this Space."
|
||||
msgstr ""
|
||||
msgstr "Загрузка файлов не разрешена для этого пространства."
|
||||
|
||||
#: .\cookbook\serializer.py:233
|
||||
msgid "You have reached your file upload limit."
|
||||
msgstr ""
|
||||
msgstr "Вы достигли лимита загрузки файлов."
|
||||
|
||||
#: .\cookbook\serializer.py:328
|
||||
msgid "Cannot modify Space owner permission."
|
||||
msgstr ""
|
||||
msgstr "Нельзя изменить разрешения владельца пространства."
|
||||
|
||||
#: .\cookbook\serializer.py:1270
|
||||
msgid "Hello"
|
||||
msgstr ""
|
||||
msgstr "Привет"
|
||||
|
||||
#: .\cookbook\serializer.py:1270
|
||||
msgid "You have been invited by "
|
||||
msgstr ""
|
||||
msgstr "Вас пригласил "
|
||||
|
||||
#: .\cookbook\serializer.py:1272
|
||||
msgid " to join their Tandoor Recipes space "
|
||||
msgstr ""
|
||||
msgstr " присоединиться к их пространству рецептов Tandoor "
|
||||
|
||||
#: .\cookbook\serializer.py:1274
|
||||
msgid "Click the following link to activate your account: "
|
||||
msgstr ""
|
||||
msgstr "Нажмите на следующую ссылку, чтобы активировать аккаунт: "
|
||||
|
||||
#: .\cookbook\serializer.py:1276
|
||||
msgid ""
|
||||
"If the link does not work use the following code to manually join the space: "
|
||||
msgstr ""
|
||||
"Если ссылка не работает, используйте следующий код для ручного присоединения "
|
||||
"к пространству: "
|
||||
|
||||
#: .\cookbook\serializer.py:1278
|
||||
msgid "The invitation is valid until "
|
||||
msgstr ""
|
||||
msgstr "Приглашение действительно до "
|
||||
|
||||
#: .\cookbook\serializer.py:1280
|
||||
msgid ""
|
||||
"Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub "
|
||||
msgstr ""
|
||||
"Tandoor Recipes — это открытый менеджер рецептов. Посмотрите его на GitHub "
|
||||
|
||||
#: .\cookbook\serializer.py:1283
|
||||
msgid "Tandoor Recipes Invite"
|
||||
msgstr ""
|
||||
msgstr "Приглашение в Tandoor Recipes"
|
||||
|
||||
#: .\cookbook\serializer.py:1426
|
||||
msgid "Existing shopping list to update"
|
||||
msgstr ""
|
||||
msgstr "Существующий список покупок для обновления"
|
||||
|
||||
#: .\cookbook\serializer.py:1428
|
||||
msgid ""
|
||||
"List of ingredient IDs from the recipe to add, if not provided all "
|
||||
"ingredients will be added."
|
||||
msgstr ""
|
||||
"Список ID ингредиентов из рецепта для добавления, если не указано — будут "
|
||||
"добавлены все."
|
||||
|
||||
#: .\cookbook\serializer.py:1430
|
||||
msgid ""
|
||||
"Providing a list_recipe ID and servings of 0 will delete that shopping list."
|
||||
msgstr ""
|
||||
"Если указать ID списка рецептов и порции 0 — этот список покупок будет "
|
||||
"удалён."
|
||||
|
||||
#: .\cookbook\serializer.py:1439
|
||||
msgid "Amount of food to add to the shopping list"
|
||||
msgstr ""
|
||||
msgstr "Количество продукта для добавления в список покупок"
|
||||
|
||||
#: .\cookbook\serializer.py:1441
|
||||
msgid "ID of unit to use for the shopping list"
|
||||
msgstr ""
|
||||
msgstr "ID единицы измерения для списка покупок"
|
||||
|
||||
#: .\cookbook\serializer.py:1443
|
||||
msgid "When set to true will delete all food from active shopping lists."
|
||||
msgstr ""
|
||||
msgstr "Если включено, удалит все продукты из активных списков покупок."
|
||||
|
||||
#: .\cookbook\tables.py:69 .\cookbook\tables.py:83
|
||||
#: .\cookbook\templates\generic\delete_template.html:7
|
||||
#: .\cookbook\templates\generic\delete_template.html:15
|
||||
#: .\cookbook\templates\generic\edit_template.html:28
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
msgstr "Удалить"
|
||||
|
||||
#: .\cookbook\templates\404.html:5
|
||||
msgid "404 Error"
|
||||
msgstr ""
|
||||
msgstr "Ошибка 404"
|
||||
|
||||
#: .\cookbook\templates\404.html:18
|
||||
msgid "The page you are looking for could not be found."
|
||||
msgstr ""
|
||||
msgstr "Страница, которую вы ищете, не найдена."
|
||||
|
||||
#: .\cookbook\templates\404.html:33
|
||||
msgid "Take me Home"
|
||||
msgstr ""
|
||||
msgstr "На главную"
|
||||
|
||||
#: .\cookbook\templates\404.html:35
|
||||
msgid "Report a Bug"
|
||||
msgstr ""
|
||||
msgstr "Сообщить об ошибке"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:6
|
||||
#: .\cookbook\templates\account\email.html:17
|
||||
msgid "E-mail Addresses"
|
||||
msgstr ""
|
||||
msgstr "Электронные адреса"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:12
|
||||
#: .\cookbook\templates\account\password_change.html:11
|
||||
@@ -690,68 +697,70 @@ msgstr ""
|
||||
#: .\cookbook\templates\socialaccount\connections.html:10
|
||||
#: .\cookbook\templates\user_settings.html:8
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
msgstr "Настройки"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:13
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
msgstr "Электронная почта"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:19
|
||||
msgid "The following e-mail addresses are associated with your account:"
|
||||
msgstr ""
|
||||
msgstr "Следующие электронные адреса связаны с вашим аккаунтом:"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:36
|
||||
msgid "Verified"
|
||||
msgstr ""
|
||||
msgstr "Подтверждён"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:38
|
||||
msgid "Unverified"
|
||||
msgstr ""
|
||||
msgstr "Не подтверждён"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:40
|
||||
msgid "Primary"
|
||||
msgstr ""
|
||||
msgstr "Основной"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:47
|
||||
msgid "Make Primary"
|
||||
msgstr ""
|
||||
msgstr "Сделать основным"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:49
|
||||
msgid "Re-send Verification"
|
||||
msgstr ""
|
||||
msgstr "Отправить подтверждение повторно"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:50
|
||||
#: .\cookbook\templates\generic\delete_template.html:57
|
||||
#: .\cookbook\templates\socialaccount\connections.html:44
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
msgstr "Удалить"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:58
|
||||
msgid "Warning:"
|
||||
msgstr ""
|
||||
msgstr "Внимание:"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:58
|
||||
msgid ""
|
||||
"You currently do not have any e-mail address set up. You should really add "
|
||||
"an e-mail address so you can receive notifications, reset your password, etc."
|
||||
msgstr ""
|
||||
"У вас пока не добавлен ни один электронный адрес. Рекомендуется добавить "
|
||||
"адрес для получения уведомлений, сброса пароля и т.д."
|
||||
|
||||
#: .\cookbook\templates\account\email.html:64
|
||||
msgid "Add E-mail Address"
|
||||
msgstr ""
|
||||
msgstr "Добавить электронный адрес"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:69
|
||||
msgid "Add E-mail"
|
||||
msgstr ""
|
||||
msgstr "Добавить e-mail"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:79
|
||||
msgid "Do you really want to remove the selected e-mail address?"
|
||||
msgstr ""
|
||||
msgstr "Вы действительно хотите удалить выбранный электронный адрес?"
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:6
|
||||
#: .\cookbook\templates\account\email_confirm.html:10
|
||||
msgid "Confirm E-mail Address"
|
||||
msgstr ""
|
||||
msgstr "Подтвердить электронный адрес"
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:16
|
||||
#, python-format
|
||||
@@ -765,7 +774,7 @@ msgstr ""
|
||||
#: .\cookbook\templates\account\email_confirm.html:22
|
||||
#: .\cookbook\templates\generic\delete_template.html:72
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
msgstr "Подтвердить"
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:29
|
||||
#, python-format
|
||||
@@ -778,7 +787,7 @@ msgstr ""
|
||||
#: .\cookbook\templates\account\login.html:8 .\cookbook\templates\base.html:388
|
||||
#: .\cookbook\templates\openid\login.html:8
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
msgstr "Логин"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:15
|
||||
#: .\cookbook\templates\account\login.html:31
|
||||
@@ -790,7 +799,7 @@ msgstr ""
|
||||
#: .\cookbook\templates\openid\login.html:26
|
||||
#: .\cookbook\templates\socialaccount\authentication_error.html:15
|
||||
msgid "Sign In"
|
||||
msgstr ""
|
||||
msgstr "Войти"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:34
|
||||
#: .\cookbook\templates\account\password_reset.html:41
|
||||
@@ -798,34 +807,35 @@ msgstr ""
|
||||
#: .\cookbook\templates\socialaccount\signup.html:8
|
||||
#: .\cookbook\templates\socialaccount\signup.html:57
|
||||
msgid "Sign Up"
|
||||
msgstr ""
|
||||
msgstr "Зарегистрироваться"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:38
|
||||
msgid "Lost your password?"
|
||||
msgstr ""
|
||||
msgstr "Забыли пароль?"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:39
|
||||
#: .\cookbook\templates\account\password_reset.html:29
|
||||
msgid "Reset My Password"
|
||||
msgstr ""
|
||||
msgstr "Сбросить мой пароль"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:50
|
||||
msgid "Social Login"
|
||||
msgstr ""
|
||||
msgstr "Вход через соцсети"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:51
|
||||
msgid "You can use any of the following providers to sign in."
|
||||
msgstr ""
|
||||
msgstr "Для входа вы можете использовать любой из следующих сервисов."
|
||||
|
||||
#: .\cookbook\templates\account\logout.html:5
|
||||
#: .\cookbook\templates\account\logout.html:9
|
||||
#: .\cookbook\templates\account\logout.html:18
|
||||
#, fuzzy
|
||||
msgid "Sign Out"
|
||||
msgstr ""
|
||||
msgstr "Выйти"
|
||||
|
||||
#: .\cookbook\templates\account\logout.html:11
|
||||
msgid "Are you sure you want to sign out?"
|
||||
msgstr ""
|
||||
msgstr "Вы действительно хотите выйти?"
|
||||
|
||||
#: .\cookbook\templates\account\password_change.html:6
|
||||
#: .\cookbook\templates\account\password_change.html:16
|
||||
@@ -835,43 +845,48 @@ msgstr ""
|
||||
#: .\cookbook\templates\account\password_reset_from_key_done.html:7
|
||||
#: .\cookbook\templates\account\password_reset_from_key_done.html:13
|
||||
msgid "Change Password"
|
||||
msgstr ""
|
||||
msgstr "Изменить пароль"
|
||||
|
||||
#: .\cookbook\templates\account\password_change.html:12
|
||||
#: .\cookbook\templates\account\password_set.html:12
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
msgstr "Пароль"
|
||||
|
||||
#: .\cookbook\templates\account\password_change.html:22
|
||||
#, fuzzy
|
||||
msgid "Forgot Password?"
|
||||
msgstr ""
|
||||
msgstr "Забыли пароль?"
|
||||
|
||||
#: .\cookbook\templates\account\password_reset.html:7
|
||||
#: .\cookbook\templates\account\password_reset.html:13
|
||||
#: .\cookbook\templates\account\password_reset_done.html:7
|
||||
#: .\cookbook\templates\account\password_reset_done.html:18
|
||||
msgid "Password Reset"
|
||||
msgstr ""
|
||||
msgstr "Сброс пароля"
|
||||
|
||||
#: .\cookbook\templates\account\password_reset.html:24
|
||||
msgid ""
|
||||
"Forgotten your password? Enter your e-mail address below, and we'll send you "
|
||||
"an e-mail allowing you to reset it."
|
||||
msgstr ""
|
||||
"Забыли пароль? Введите свой электронный адрес ниже, и мы отправим вам письмо "
|
||||
"для сброса пароля."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset.html:32
|
||||
msgid "Password reset is disabled on this instance."
|
||||
msgstr ""
|
||||
msgstr "Сброс пароля отключён для этого экземпляра."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_done.html:25
|
||||
msgid ""
|
||||
"We have sent you an e-mail. Please contact us if you do not receive it "
|
||||
"within a few minutes."
|
||||
msgstr ""
|
||||
"Мы отправили вам электронное письмо. Пожалуйста, свяжитесь с нами, если не "
|
||||
"получите его в течение нескольких минут."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:13
|
||||
msgid "Bad Token"
|
||||
msgstr ""
|
||||
msgstr "Неверный токен"
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:25
|
||||
#, python-format
|
||||
@@ -881,71 +896,76 @@ msgid ""
|
||||
" Please request a <a href=\"%(passwd_reset_url)s\">new "
|
||||
"password reset</a>."
|
||||
msgstr ""
|
||||
"Ссылка для сброса пароля недействительна, возможно, она уже была "
|
||||
"использована.\n"
|
||||
" Пожалуйста, запросите <a href=\"%(passwd_reset_url)s\">новую "
|
||||
"ссылку для сброса пароля</a>."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:33
|
||||
msgid "change password"
|
||||
msgstr ""
|
||||
msgstr "изменить пароль"
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:36
|
||||
#: .\cookbook\templates\account\password_reset_from_key_done.html:19
|
||||
msgid "Your password is now changed."
|
||||
msgstr ""
|
||||
msgstr "Ваш пароль изменён."
|
||||
|
||||
#: .\cookbook\templates\account\password_set.html:6
|
||||
#: .\cookbook\templates\account\password_set.html:16
|
||||
#: .\cookbook\templates\account\password_set.html:21
|
||||
msgid "Set Password"
|
||||
msgstr ""
|
||||
msgstr "Установить пароль"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:6
|
||||
#, fuzzy
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
msgstr "Зарегистрироваться"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:12
|
||||
msgid "Create an Account"
|
||||
msgstr ""
|
||||
msgstr "Создать аккаунт"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:42
|
||||
#: .\cookbook\templates\socialaccount\signup.html:33
|
||||
msgid "I accept the follwoing"
|
||||
msgstr ""
|
||||
msgstr "Я принимаю следующие"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:45
|
||||
#: .\cookbook\templates\socialaccount\signup.html:36
|
||||
msgid "Terms and Conditions"
|
||||
msgstr ""
|
||||
msgstr "Условия и положения"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:48
|
||||
#: .\cookbook\templates\socialaccount\signup.html:39
|
||||
msgid "and"
|
||||
msgstr ""
|
||||
msgstr "и"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:52
|
||||
#: .\cookbook\templates\socialaccount\signup.html:43
|
||||
msgid "Privacy Policy"
|
||||
msgstr ""
|
||||
msgstr "Политику конфиденциальности"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:65
|
||||
msgid "Create User"
|
||||
msgstr ""
|
||||
msgstr "Создать пользователя"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:69
|
||||
msgid "Already have an account?"
|
||||
msgstr ""
|
||||
msgstr "Уже есть аккаунт?"
|
||||
|
||||
#: .\cookbook\templates\account\signup_closed.html:5
|
||||
#: .\cookbook\templates\account\signup_closed.html:11
|
||||
msgid "Sign Up Closed"
|
||||
msgstr ""
|
||||
msgstr "Регистрация закрыта"
|
||||
|
||||
#: .\cookbook\templates\account\signup_closed.html:13
|
||||
msgid "We are sorry, but the sign up is currently closed."
|
||||
msgstr ""
|
||||
msgstr "К сожалению, регистрация сейчас закрыта."
|
||||
|
||||
#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:378
|
||||
#: .\cookbook\templates\rest_framework\api.html:11
|
||||
msgid "API Documentation"
|
||||
msgstr ""
|
||||
msgstr "Документация API"
|
||||
|
||||
#: .\cookbook\templates\base.html:110 .\cookbook\templates\index.html:87
|
||||
msgid "Recipes"
|
||||
@@ -953,36 +973,38 @@ msgstr "Рецепты"
|
||||
|
||||
#: .\cookbook\templates\base.html:161 .\cookbook\views\lists.py:120
|
||||
msgid "Foods"
|
||||
msgstr ""
|
||||
msgstr "Продукты"
|
||||
|
||||
#: .\cookbook\templates\base.html:173 .\cookbook\views\lists.py:137
|
||||
#, fuzzy
|
||||
msgid "Units"
|
||||
msgstr ""
|
||||
msgstr "Единицы измерения"
|
||||
|
||||
#: .\cookbook\templates\base.html:187
|
||||
msgid "Supermarket"
|
||||
msgstr ""
|
||||
msgstr "Супермаркет"
|
||||
|
||||
#: .\cookbook\templates\base.html:199
|
||||
msgid "Supermarket Category"
|
||||
msgstr ""
|
||||
msgstr "Категория супермаркета"
|
||||
|
||||
#: .\cookbook\templates\base.html:211 .\cookbook\views\lists.py:186
|
||||
#, fuzzy
|
||||
msgid "Automations"
|
||||
msgstr ""
|
||||
msgstr "Автоматизация"
|
||||
|
||||
#: .\cookbook\templates\base.html:225 .\cookbook\views\lists.py:222
|
||||
msgid "Files"
|
||||
msgstr ""
|
||||
msgstr "Файлы"
|
||||
|
||||
#: .\cookbook\templates\base.html:237
|
||||
msgid "Batch Edit"
|
||||
msgstr ""
|
||||
msgstr "Пакетное редактирование"
|
||||
|
||||
#: .\cookbook\templates\base.html:249 .\cookbook\templates\history.html:6
|
||||
#: .\cookbook\templates\history.html:14
|
||||
msgid "History"
|
||||
msgstr ""
|
||||
msgstr "История"
|
||||
|
||||
#: .\cookbook\templates\base.html:263
|
||||
#: .\cookbook\templates\ingredient_editor.html:7
|
||||
@@ -996,58 +1018,58 @@ msgstr "Ингредиенты"
|
||||
#: .\cookbook\templates\export_response.html:7
|
||||
#: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
msgstr "Экспортировать"
|
||||
|
||||
#: .\cookbook\templates\base.html:287
|
||||
msgid "Properties"
|
||||
msgstr ""
|
||||
msgstr "Свойства"
|
||||
|
||||
#: .\cookbook\templates\base.html:301 .\cookbook\views\lists.py:255
|
||||
msgid "Unit Conversions"
|
||||
msgstr ""
|
||||
msgstr "Конвертация единиц"
|
||||
|
||||
#: .\cookbook\templates\base.html:318 .\cookbook\templates\index.html:47
|
||||
msgid "Import Recipe"
|
||||
msgstr ""
|
||||
msgstr "Импортировать рецепт"
|
||||
|
||||
#: .\cookbook\templates\base.html:320
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
msgstr "Создать"
|
||||
|
||||
#: .\cookbook\templates\base.html:333
|
||||
#: .\cookbook\templates\generic\list_template.html:14
|
||||
msgid "External Recipes"
|
||||
msgstr ""
|
||||
msgstr "Внешние рецепты"
|
||||
|
||||
#: .\cookbook\templates\base.html:336 .\cookbook\templates\space_manage.html:15
|
||||
msgid "Space Settings"
|
||||
msgstr ""
|
||||
msgstr "Настройки пространства"
|
||||
|
||||
#: .\cookbook\templates\base.html:340
|
||||
msgid "External Connectors"
|
||||
msgstr ""
|
||||
msgstr "Внешние соединения"
|
||||
|
||||
#: .\cookbook\templates\base.html:345 .\cookbook\templates\system.html:13
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
msgstr "Система"
|
||||
|
||||
#: .\cookbook\templates\base.html:347
|
||||
msgid "Admin"
|
||||
msgstr ""
|
||||
msgstr "Администрирование"
|
||||
|
||||
#: .\cookbook\templates\base.html:351
|
||||
#: .\cookbook\templates\space_overview.html:25
|
||||
msgid "Your Spaces"
|
||||
msgstr ""
|
||||
msgstr "Ваши пространства"
|
||||
|
||||
#: .\cookbook\templates\base.html:362
|
||||
#: .\cookbook\templates\space_overview.html:6
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
msgstr "Обзор"
|
||||
|
||||
#: .\cookbook\templates\base.html:372
|
||||
msgid "Markdown Guide"
|
||||
msgstr ""
|
||||
msgstr "Руководство по Markdown"
|
||||
|
||||
#: .\cookbook\templates\base.html:374
|
||||
msgid "GitHub"
|
||||
@@ -1055,53 +1077,55 @@ msgstr "GitHub"
|
||||
|
||||
#: .\cookbook\templates\base.html:376
|
||||
msgid "Translate Tandoor"
|
||||
msgstr ""
|
||||
msgstr "Перевести Tandoor"
|
||||
|
||||
#: .\cookbook\templates\base.html:380
|
||||
msgid "API Browser"
|
||||
msgstr ""
|
||||
msgstr "Браузер API"
|
||||
|
||||
#: .\cookbook\templates\base.html:383
|
||||
msgid "Log out"
|
||||
msgstr ""
|
||||
msgstr "Выйти"
|
||||
|
||||
#: .\cookbook\templates\base.html:406
|
||||
msgid "You are using the free version of Tandor"
|
||||
msgstr ""
|
||||
msgstr "Вы используете бесплатную версию Tandor"
|
||||
|
||||
#: .\cookbook\templates\base.html:407
|
||||
msgid "Upgrade Now"
|
||||
msgstr ""
|
||||
msgstr "Обновить сейчас"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:6
|
||||
msgid "Batch edit Category"
|
||||
msgstr ""
|
||||
msgstr "Пакетное редактирование категорий"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:15
|
||||
msgid "Batch edit Recipes"
|
||||
msgstr ""
|
||||
msgstr "Пакетное редактирование рецептов"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:20
|
||||
msgid "Add the specified keywords to all recipes containing a word"
|
||||
msgstr ""
|
||||
msgstr "Добавить указанные ключевые слова ко всем рецептам, содержащим слово"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:75
|
||||
msgid "Sync"
|
||||
msgstr ""
|
||||
msgstr "Синхронизировать"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:10
|
||||
msgid "Manage watched Folders"
|
||||
msgstr ""
|
||||
msgstr "Управлять отслеживаемыми папками"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:14
|
||||
msgid ""
|
||||
"On this Page you can manage all storage folder locations that should be "
|
||||
"monitored and synced."
|
||||
msgstr ""
|
||||
"На этой странице вы можете управлять всеми папками для хранения, которые "
|
||||
"нужно отслеживать и синхронизировать."
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:16
|
||||
msgid "The path must be in the following format"
|
||||
msgstr ""
|
||||
msgstr "Путь должен быть в следующем формате"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:20
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:14
|
||||
@@ -1109,128 +1133,131 @@ msgstr ""
|
||||
#: .\cookbook\templates\generic\new_template.html:23
|
||||
#: .\cookbook\templates\settings.html:57
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Сохранить"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:21
|
||||
msgid "Manage External Storage"
|
||||
msgstr ""
|
||||
msgstr "Управлять внешним хранилищем"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:28
|
||||
msgid "Sync Now!"
|
||||
msgstr ""
|
||||
msgstr "Синхронизировать сейчас!"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:29
|
||||
msgid "Show Recipes"
|
||||
msgstr ""
|
||||
msgstr "Показать рецепты"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:30
|
||||
msgid "Show Log"
|
||||
msgstr ""
|
||||
msgstr "Показать журнал"
|
||||
|
||||
#: .\cookbook\templates\batch\waiting.html:4
|
||||
#: .\cookbook\templates\batch\waiting.html:10
|
||||
msgid "Importing Recipes"
|
||||
msgstr ""
|
||||
msgstr "Импорт рецептов"
|
||||
|
||||
#: .\cookbook\templates\batch\waiting.html:28
|
||||
msgid ""
|
||||
"This can take a few minutes, depending on the number of recipes in sync, "
|
||||
"please wait."
|
||||
msgstr ""
|
||||
"Это может занять несколько минут, в зависимости от количества рецептов в "
|
||||
"синхронизации. Пожалуйста, подождите."
|
||||
|
||||
#: .\cookbook\templates\books.html:7
|
||||
msgid "Recipe Books"
|
||||
msgstr ""
|
||||
msgstr "Кулинарные книги"
|
||||
|
||||
#: .\cookbook\templates\export.html:7 .\cookbook\templates\test2.html:6
|
||||
msgid "Export Recipes"
|
||||
msgstr ""
|
||||
msgstr "Экспорт рецептов"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:5
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:9
|
||||
msgid "Import new Recipe"
|
||||
msgstr ""
|
||||
msgstr "Импорт новых рецептов"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:7
|
||||
msgid "Edit Recipe"
|
||||
msgstr ""
|
||||
msgstr "Редактировать рецепт"
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:21
|
||||
#, python-format
|
||||
msgid "Are you sure you want to delete the %(title)s: <b>%(object)s</b> "
|
||||
msgstr ""
|
||||
msgstr "Вы уверены, что хотите удалить %(title)s: <b>%(object)s</b> "
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:22
|
||||
msgid "This cannot be undone!"
|
||||
msgstr ""
|
||||
msgstr "Это действие необратимо!"
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:27
|
||||
msgid "Protected"
|
||||
msgstr ""
|
||||
msgstr "Защищённый"
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:42
|
||||
msgid "Cascade"
|
||||
msgstr ""
|
||||
msgstr "Каскад"
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:73
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgstr "Отмена"
|
||||
|
||||
#: .\cookbook\templates\generic\edit_template.html:6
|
||||
#: .\cookbook\templates\generic\edit_template.html:14
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Редактировать"
|
||||
|
||||
#: .\cookbook\templates\generic\edit_template.html:32
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
msgstr "Просмотреть"
|
||||
|
||||
#: .\cookbook\templates\generic\edit_template.html:36
|
||||
msgid "Delete original file"
|
||||
msgstr ""
|
||||
msgstr "Удалить оригинальный файл"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:6
|
||||
#: .\cookbook\templates\generic\list_template.html:22
|
||||
msgid "List"
|
||||
msgstr ""
|
||||
msgstr "Список"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:36
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Фильтр"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:41
|
||||
msgid "Import all"
|
||||
msgstr ""
|
||||
msgstr "Импортировать всё"
|
||||
|
||||
#: .\cookbook\templates\generic\new_template.html:6
|
||||
#: .\cookbook\templates\generic\new_template.html:14
|
||||
msgid "New"
|
||||
msgstr ""
|
||||
msgstr "Новый"
|
||||
|
||||
#: .\cookbook\templates\generic\table_template.html:76
|
||||
msgid "previous"
|
||||
msgstr ""
|
||||
msgstr "предыдущий"
|
||||
|
||||
#: .\cookbook\templates\generic\table_template.html:98
|
||||
msgid "next"
|
||||
msgstr ""
|
||||
msgstr "следующий"
|
||||
|
||||
#: .\cookbook\templates\history.html:20
|
||||
msgid "View Log"
|
||||
msgstr ""
|
||||
msgstr "Просмотреть журнал"
|
||||
|
||||
#: .\cookbook\templates\history.html:24
|
||||
#, fuzzy
|
||||
msgid "Cook Log"
|
||||
msgstr ""
|
||||
msgstr "Журнал приготовления"
|
||||
|
||||
#: .\cookbook\templates\import_response.html:7 .\cookbook\views\delete.py:90
|
||||
#: .\cookbook\views\edit.py:174
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
msgstr "Импортировать"
|
||||
|
||||
#: .\cookbook\templates\include\storage_backend_warning.html:4
|
||||
msgid "Security Warning"
|
||||
msgstr ""
|
||||
msgstr "Предупреждение о безопасности"
|
||||
|
||||
#: .\cookbook\templates\include\storage_backend_warning.html:5
|
||||
msgid ""
|
||||
@@ -1247,32 +1274,32 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\index.html:29
|
||||
msgid "Search recipe ..."
|
||||
msgstr ""
|
||||
msgstr "Поиск рецепта ..."
|
||||
|
||||
#: .\cookbook\templates\index.html:44
|
||||
msgid "New Recipe"
|
||||
msgstr ""
|
||||
msgstr "Новый рецепт"
|
||||
|
||||
#: .\cookbook\templates\index.html:53
|
||||
msgid "Advanced Search"
|
||||
msgstr ""
|
||||
msgstr "Расширенный поиск"
|
||||
|
||||
#: .\cookbook\templates\index.html:57
|
||||
msgid "Reset Search"
|
||||
msgstr ""
|
||||
msgstr "Сбросить поиск"
|
||||
|
||||
#: .\cookbook\templates\index.html:85
|
||||
msgid "Last viewed"
|
||||
msgstr ""
|
||||
msgstr "Недавно просмотренные"
|
||||
|
||||
#: .\cookbook\templates\index.html:94
|
||||
msgid "Log in to view recipes"
|
||||
msgstr ""
|
||||
msgstr "Войдите, чтобы просмотреть рецепты"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:5
|
||||
#: .\cookbook\templates\markdown_info.html:13
|
||||
msgid "Markdown Info"
|
||||
msgstr ""
|
||||
msgstr "Информация о Markdown"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:14
|
||||
msgid ""
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||
"PO-Revision-Date: 2025-02-07 08:58+0000\n"
|
||||
"Last-Translator: Mattias G <mattias.granlund@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-08-10 11:36+0000\n"
|
||||
"Last-Translator: Elias Sjögreen <eliassjogreen1@gmail.com>\n"
|
||||
"Language-Team: Swedish <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/sv/>\n"
|
||||
"Language: sv\n"
|
||||
@@ -641,6 +641,8 @@ msgstr "ID eller enhet att använda för inköpslistan"
|
||||
#: .\cookbook\serializer.py:1443
|
||||
msgid "When set to true will delete all food from active shopping lists."
|
||||
msgstr ""
|
||||
"Om det här alternativet är aktiverat kommer alla matvaror att raderas från "
|
||||
"de aktiva inköpslistorna."
|
||||
|
||||
#: .\cookbook\tables.py:69 .\cookbook\tables.py:83
|
||||
#: .\cookbook\templates\generic\delete_template.html:7
|
||||
@@ -723,10 +725,13 @@ msgid ""
|
||||
"You currently do not have any e-mail address set up. You should really add "
|
||||
"an e-mail address so you can receive notifications, reset your password, etc."
|
||||
msgstr ""
|
||||
"Just nu har du inga e-post adresser konfigurerade. Du borde verkligen lägga "
|
||||
"till en e-post adress så att du kan får notiser, återställa ditt lösenord, "
|
||||
"mm."
|
||||
|
||||
#: .\cookbook\templates\account\email.html:64
|
||||
msgid "Add E-mail Address"
|
||||
msgstr ""
|
||||
msgstr "Lägg till en e-post adress"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:69
|
||||
msgid "Add E-mail"
|
||||
@@ -734,12 +739,12 @@ msgstr "Lägg till email"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:79
|
||||
msgid "Do you really want to remove the selected e-mail address?"
|
||||
msgstr ""
|
||||
msgstr "Vill du verkligen ta bort den valda e-postadressen?"
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:6
|
||||
#: .\cookbook\templates\account\email_confirm.html:10
|
||||
msgid "Confirm E-mail Address"
|
||||
msgstr ""
|
||||
msgstr "Bekräfta e-postadress"
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:16
|
||||
#, python-format
|
||||
@@ -749,6 +754,10 @@ msgid ""
|
||||
"for user %(user_display)s\n"
|
||||
" ."
|
||||
msgstr ""
|
||||
"Vänligen bekräfra att\n"
|
||||
" <a href=\"mailto:%(email)s\">%(email)s</a> är e-postadressen för "
|
||||
"användaren %(user_display)s\n"
|
||||
" ."
|
||||
|
||||
#: .\cookbook\templates\account\email_confirm.html:22
|
||||
#: .\cookbook\templates\generic\delete_template.html:72
|
||||
@@ -762,6 +771,9 @@ msgid ""
|
||||
" <a href=\"%(email_url)s\">issue a new e-mail confirmation "
|
||||
"request</a>."
|
||||
msgstr ""
|
||||
"Denna e-post bekräftelselänk är utgången eller felaktig. Vänligen\n"
|
||||
" <a href=\"%(email_url)s\">skapa en ny "
|
||||
"e-postbekräftelsebegäran</a>."
|
||||
|
||||
#: .\cookbook\templates\account\login.html:8 .\cookbook\templates\base.html:388
|
||||
#: .\cookbook\templates\openid\login.html:8
|
||||
@@ -785,19 +797,17 @@ msgstr "Logga in"
|
||||
#: .\cookbook\templates\account\password_reset_done.html:33
|
||||
#: .\cookbook\templates\socialaccount\signup.html:8
|
||||
#: .\cookbook\templates\socialaccount\signup.html:57
|
||||
#, fuzzy
|
||||
#| msgid "Sign In"
|
||||
msgid "Sign Up"
|
||||
msgstr "Logga in"
|
||||
msgstr "Registrera dig"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:38
|
||||
msgid "Lost your password?"
|
||||
msgstr ""
|
||||
msgstr "Glömt ditt lösenord?"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:39
|
||||
#: .\cookbook\templates\account\password_reset.html:29
|
||||
msgid "Reset My Password"
|
||||
msgstr ""
|
||||
msgstr "Återställ mitt lösenord"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:50
|
||||
msgid "Social Login"
|
||||
@@ -824,10 +834,8 @@ msgstr "Är du säker på att du vill logga ut?"
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:13
|
||||
#: .\cookbook\templates\account\password_reset_from_key_done.html:7
|
||||
#: .\cookbook\templates\account\password_reset_from_key_done.html:13
|
||||
#, fuzzy
|
||||
#| msgid "Changes saved!"
|
||||
msgid "Change Password"
|
||||
msgstr "Ändringar sparade!"
|
||||
msgstr "Ändra lösenord"
|
||||
|
||||
#: .\cookbook\templates\account\password_change.html:12
|
||||
#: .\cookbook\templates\account\password_set.html:12
|
||||
@@ -850,18 +858,20 @@ msgid ""
|
||||
"Forgotten your password? Enter your e-mail address below, and we'll send you "
|
||||
"an e-mail allowing you to reset it."
|
||||
msgstr ""
|
||||
"Glömt ditt lösenord? Ange din e-postadress nedanför så skickar vi ett "
|
||||
"återställningmail."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset.html:32
|
||||
#, fuzzy
|
||||
#| msgid "Password reset is not implemented for the time being!"
|
||||
msgid "Password reset is disabled on this instance."
|
||||
msgstr "Återställning av lösenord har ännu inte lagts till!"
|
||||
msgstr "Återställning av lösenord är avaktiverat på denna instans."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_done.html:25
|
||||
msgid ""
|
||||
"We have sent you an e-mail. Please contact us if you do not receive it "
|
||||
"within a few minutes."
|
||||
msgstr ""
|
||||
"Vi har skickat ett e-postmeddelande till dig. Om du inte har fått det inom "
|
||||
"några minuter, vänligen kontakta oss."
|
||||
|
||||
#: .\cookbook\templates\account\password_reset_from_key.html:13
|
||||
#, fuzzy
|
||||
|
||||
@@ -8,22 +8,22 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-01 15:04+0200\n"
|
||||
"PO-Revision-Date: 2024-11-04 10:29+0000\n"
|
||||
"Last-Translator: Johnny Ip <ip.iohnny@gmail.com>\n"
|
||||
"Language-Team: Chinese (Traditional) <http://translate.tandoor.dev/projects/"
|
||||
"tandoor/recipes-backend/zh_Hant/>\n"
|
||||
"PO-Revision-Date: 2025-08-02 07:49+0000\n"
|
||||
"Last-Translator: TC Kuo <tckuo7@gmail.com>\n"
|
||||
"Language-Team: Chinese (Traditional Han script) <http://translate.tandoor."
|
||||
"dev/projects/tandoor/recipes-backend/zh_Hant/>\n"
|
||||
"Language: zh_Hant\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Weblate 5.6.2\n"
|
||||
"X-Generator: Weblate 5.8.4\n"
|
||||
|
||||
#: .\cookbook\forms.py:45
|
||||
msgid ""
|
||||
"Both fields are optional. If none are given the username will be displayed "
|
||||
"instead"
|
||||
msgstr "這兩個字段都是可選的。如果沒有輸入,將顯示用戶名"
|
||||
msgstr "這兩個欄位都是可選的。如果沒有輸入,將顯示用戶名"
|
||||
|
||||
#: .\cookbook\forms.py:62 .\cookbook\forms.py:246
|
||||
msgid "Name"
|
||||
@@ -31,7 +31,7 @@ msgstr "名字"
|
||||
|
||||
#: .\cookbook\forms.py:62 .\cookbook\forms.py:246 .\cookbook\views\lists.py:103
|
||||
msgid "Keywords"
|
||||
msgstr "關鍵詞"
|
||||
msgstr "關鍵字"
|
||||
|
||||
#: .\cookbook\forms.py:62
|
||||
msgid "Preparation time in minutes"
|
||||
@@ -51,14 +51,13 @@ msgstr "存儲ID"
|
||||
|
||||
#: .\cookbook\forms.py:93
|
||||
msgid "Default"
|
||||
msgstr "默認"
|
||||
msgstr "預設"
|
||||
|
||||
#: .\cookbook\forms.py:121
|
||||
msgid ""
|
||||
"To prevent duplicates recipes with the same name as existing ones are "
|
||||
"ignored. Check this box to import everything."
|
||||
msgstr ""
|
||||
"為防止重復,忽略與現有同名的菜譜。選中此框可導入所有內容(包括同名菜譜)。"
|
||||
msgstr "為防止重複,忽略與現有同名的食譜。選中此框可導入所有內容(包括同名食譜)。"
|
||||
|
||||
#: .\cookbook\forms.py:143
|
||||
msgid "Add your comment: "
|
||||
@@ -241,7 +240,7 @@ msgstr "你沒有必要的權限來查看這個頁面!"
|
||||
#: .\cookbook\helper\permission_helper.py:237
|
||||
#: .\cookbook\helper\permission_helper.py:252
|
||||
msgid "You cannot interact with this object as it is not owned by you!"
|
||||
msgstr "你不能與此對象交互,因為它不屬於你!"
|
||||
msgstr "你不能與此對象互動,因為它不屬於你!"
|
||||
|
||||
#: .\cookbook\helper\permission_helper.py:402
|
||||
msgid "You have reached the maximum number of recipes for your space."
|
||||
@@ -311,16 +310,16 @@ msgstr "在導入過程中發生了一個意外的錯誤。請確認你上傳的
|
||||
|
||||
#: .\cookbook\integration\integration.py:217
|
||||
msgid "The following recipes were ignored because they already existed:"
|
||||
msgstr "以下菜譜被忽略了,因為它們已經存在了:"
|
||||
msgstr "以下食譜被忽略了,因為它們已經存在了:"
|
||||
|
||||
#: .\cookbook\integration\integration.py:221
|
||||
#, python-format
|
||||
msgid "Imported %s recipes."
|
||||
msgstr "導入了%s菜譜。"
|
||||
msgstr "導入了%s食譜。"
|
||||
|
||||
#: .\cookbook\integration\openeats.py:28
|
||||
msgid "Recipe source:"
|
||||
msgstr "菜譜來源:"
|
||||
msgstr "食譜來源:"
|
||||
|
||||
#: .\cookbook\integration\paprika.py:49
|
||||
msgid "Notes"
|
||||
@@ -328,7 +327,7 @@ msgstr "說明"
|
||||
|
||||
#: .\cookbook\integration\paprika.py:52
|
||||
msgid "Nutritional Information"
|
||||
msgstr "營養信息"
|
||||
msgstr "營養資訊"
|
||||
|
||||
#: .\cookbook\integration\paprika.py:56
|
||||
msgid "Source"
|
||||
@@ -645,7 +644,7 @@ msgstr "電子郵件地址"
|
||||
#: .\cookbook\templates\socialaccount\connections.html:10
|
||||
#: .\cookbook\templates\user_settings.html:8
|
||||
msgid "Settings"
|
||||
msgstr "設置"
|
||||
msgstr "設定"
|
||||
|
||||
#: .\cookbook\templates\account\email.html:13
|
||||
msgid "Email"
|
||||
@@ -1878,7 +1877,7 @@ msgstr "你可以被邀請加入現有空間或創建自己的空間。"
|
||||
|
||||
#: .\cookbook\templates\space_overview.html:53
|
||||
msgid "Owner"
|
||||
msgstr "所有者"
|
||||
msgstr "擁有者"
|
||||
|
||||
#: .\cookbook\templates\space_overview.html:57
|
||||
msgid "Leave Space"
|
||||
|
||||
34
cookbook/migrations/0223_auto_20250831_1111.py
Normal file
34
cookbook/migrations/0223_auto_20250831_1111.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 4.2.22 on 2025-08-31 09:11
|
||||
|
||||
from django.db import migrations
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
|
||||
def migrate_comments(apps, schema_editor):
|
||||
with scopes_disabled():
|
||||
Comment = apps.get_model('cookbook', 'Comment')
|
||||
CookLog = apps.get_model('cookbook', 'CookLog')
|
||||
|
||||
cook_logs = []
|
||||
|
||||
for c in Comment.objects.all():
|
||||
cook_logs.append(CookLog(
|
||||
recipe=c.recipe,
|
||||
created_by=c.created_by,
|
||||
created_at=c.created_at,
|
||||
comment=c.text,
|
||||
space=c.recipe.space,
|
||||
))
|
||||
|
||||
CookLog.objects.bulk_create(cook_logs, unique_fields=('recipe', 'comment', 'created_at', 'created_by'))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0222_alter_shoppinglistrecipe_created_by_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_comments),
|
||||
]
|
||||
@@ -592,7 +592,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
||||
fields = (
|
||||
'id', 'name', 'label', 'description', 'image', 'parent', 'numchild', 'numrecipe', 'created_at',
|
||||
'updated_at', 'full_name')
|
||||
read_only_fields = ('id', 'label', 'numchild', 'parent', 'image')
|
||||
read_only_fields = ('id', 'label', 'numchild', 'numrecipe', 'parent', 'image')
|
||||
|
||||
|
||||
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin, OpenDataModelMixin):
|
||||
@@ -787,7 +787,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
if plural_name := validated_data.pop('plural_name', None):
|
||||
plural_name = plural_name.strip()
|
||||
|
||||
if food := Food.objects.filter(Q(name=name) | Q(plural_name=name)).first():
|
||||
if food := Food.objects.filter(Q(name__iexact=name) | Q(plural_name__iexact=name)).first():
|
||||
return food
|
||||
|
||||
space = validated_data.pop('space', self.context['request'].space)
|
||||
@@ -1038,7 +1038,7 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
|
||||
fields = (
|
||||
'id', 'name', 'description', 'image', 'keywords', 'working_time',
|
||||
'waiting_time', 'created_by', 'created_at', 'updated_at',
|
||||
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
|
||||
'internal', 'private','servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
|
||||
)
|
||||
# TODO having these readonly fields makes "RecipeOverview.ts" (API Client) not generate the RecipeOverviewToJSON second else block which leads to errors when using the api
|
||||
# TODO find a solution (custom schema?) to have these fields readonly (to save performance) and generate a proper client (two serializers would probably do the trick)
|
||||
@@ -1112,6 +1112,28 @@ class RecipeImportSerializer(SpacedModelSerializer):
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class RecipeBatchUpdateSerializer(serializers.Serializer):
|
||||
recipes = serializers.ListField(child=serializers.IntegerField())
|
||||
keywords_add = serializers.ListField(child=serializers.IntegerField())
|
||||
keywords_remove = serializers.ListField(child=serializers.IntegerField())
|
||||
keywords_set = serializers.ListField(child=serializers.IntegerField())
|
||||
keywords_remove_all = serializers.BooleanField(default=False)
|
||||
|
||||
working_time = serializers.IntegerField(required=False, allow_null=True)
|
||||
waiting_time = serializers.IntegerField(required=False, allow_null=True)
|
||||
servings = serializers.IntegerField(required=False, allow_null=True)
|
||||
servings_text = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||||
|
||||
private = serializers.BooleanField(required=False, allow_null=True)
|
||||
shared_add = serializers.ListField(child=serializers.IntegerField())
|
||||
shared_remove = serializers.ListField(child=serializers.IntegerField())
|
||||
shared_set = serializers.ListField(child=serializers.IntegerField())
|
||||
shared_remove_all = serializers.BooleanField(default=False)
|
||||
|
||||
show_ingredient_overview = serializers.BooleanField(required=False, allow_null=True)
|
||||
clear_description = serializers.BooleanField(required=False, allow_null=True)
|
||||
|
||||
|
||||
class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||
shared = UserSerializer(many=True, required=False)
|
||||
|
||||
@@ -1223,8 +1245,8 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||
|
||||
|
||||
class AutoMealPlanSerializer(serializers.Serializer):
|
||||
start_date = serializers.DateField()
|
||||
end_date = serializers.DateField()
|
||||
start_date = serializers.DateTimeField()
|
||||
end_date = serializers.DateTimeField()
|
||||
meal_type_id = serializers.IntegerField()
|
||||
keyword_ids = serializers.ListField()
|
||||
servings = CustomDecimalField()
|
||||
@@ -1480,7 +1502,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
|
||||
fields = (
|
||||
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by',
|
||||
'created_at',)
|
||||
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)
|
||||
read_only_fields = ('id', 'uuid', 'used_by', 'created_by', 'created_at',)
|
||||
|
||||
|
||||
# CORS, REST and Scopes aren't currently working
|
||||
@@ -1768,6 +1790,7 @@ class RecipeFromSourceResponseSerializer(serializers.Serializer):
|
||||
class AiImportSerializer(serializers.Serializer):
|
||||
file = serializers.FileField(allow_null=True)
|
||||
text = serializers.CharField(allow_null=True, allow_blank=True)
|
||||
recipe_id = serializers.CharField(allow_null=True, allow_blank=True)
|
||||
|
||||
|
||||
class ExportRequestSerializer(serializers.Serializer):
|
||||
|
||||
@@ -51,11 +51,6 @@
|
||||
{# {% endif %}#}
|
||||
<p class="card-text"><small
|
||||
class="text-muted">{% trans 'Owner' %}: {{ us.space.created_by }}</small>
|
||||
{% if us.space.created_by != us.user %}
|
||||
<p class="card-text"><small
|
||||
class="text-muted"><a
|
||||
href="{% url 'delete_user_space' us.pk %}">{% trans 'Leave Space' %}</a></small>
|
||||
{% endif %}
|
||||
<!--TODO add direct link to management page -->
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -53,6 +53,17 @@
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
|
||||
<h3 class="mt-5">{% trans 'Plugins' %}</h3>
|
||||
Clone the plugin using git into the <code>recipe/plugin/</code> folder (Docker mount <code>/opt/recipe/recipes/plugins</code> to the host system and clone into it).
|
||||
<table class="table table-bordered">
|
||||
{% for p in plugins %}
|
||||
<tr>
|
||||
<td><a href="{{ p.github }}">{{ p.name }}</a> <br/>{{ p.base_path }}</td>
|
||||
<td><a href="{% url 'view_plugin_update' %}?module={{ p.module }}" class="btn btn-primary">Git Pull</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<h4 class="mt-3">{% trans 'Media Serving' %} <span class="badge text-bg-{% if gunicorn_media %}danger{% else %}success{% endif %}">{% if gunicorn_media %}
|
||||
{% trans 'Warning' %}{% else %}{% trans 'Ok' %}{% endif %}</span></h4>
|
||||
{% if gunicorn_media %}
|
||||
|
||||
@@ -80,8 +80,9 @@ urlpatterns = [
|
||||
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
|
||||
path('no-perm/', views.no_perm, name='view_no_perm'),
|
||||
path('invite/<slug:token>', views.invite_link, name='view_invite'),
|
||||
path('system/', views.system, name='view_system'),
|
||||
|
||||
path('system/', views.system, name='view_system'),
|
||||
path('plugin/update/', views.plugin_update, name='view_plugin_update'),
|
||||
|
||||
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
|
||||
|
||||
@@ -125,10 +126,6 @@ urlpatterns = [
|
||||
|
||||
]
|
||||
|
||||
if DEBUG:
|
||||
urlpatterns.append(path('test/', views.test, name='view_test'))
|
||||
urlpatterns.append(path('test2/', views.test2, name='view_test2'))
|
||||
|
||||
# catchall view for new frontend
|
||||
urlpatterns += [
|
||||
path('', views.index, name='index'),
|
||||
|
||||
@@ -110,7 +110,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
|
||||
UserSerializer, UserSpaceSerializer, ViewLogSerializer,
|
||||
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer, FdcQuerySerializer,
|
||||
AiImportSerializer, ImportOpenDataSerializer, ImportOpenDataMetaDataSerializer, ImportOpenDataResponseSerializer, ExportRequestSerializer,
|
||||
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer
|
||||
RecipeImportSerializer, ConnectorConfigSerializer, SearchPreferenceSerializer, SearchFieldsSerializer, RecipeBatchUpdateSerializer
|
||||
)
|
||||
from cookbook.version_info import TANDOOR_VERSION
|
||||
from cookbook.views.import_export import get_integration
|
||||
@@ -411,6 +411,7 @@ class MergeMixin(ViewSetMixin):
|
||||
description='Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.',
|
||||
type=int),
|
||||
OpenApiParameter(name='tree', description='Return all self and children of {obj} with ID [int].', type=int),
|
||||
OpenApiParameter(name='root_tree', description='Return all items belonging to the tree of the given {obj} id', type=int),
|
||||
]),
|
||||
move=extend_schema(parameters=[
|
||||
OpenApiParameter(name="parent", description='The ID of the desired parent of the {obj}.', type=OpenApiTypes.INT,
|
||||
@@ -423,6 +424,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin):
|
||||
def get_queryset(self):
|
||||
root = self.request.query_params.get('root', None)
|
||||
tree = self.request.query_params.get('tree', None)
|
||||
root_tree = self.request.query_params.get('root_tree', None)
|
||||
|
||||
if root:
|
||||
if root.isnumeric():
|
||||
@@ -441,10 +443,21 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin):
|
||||
self.queryset = self.model.objects.get(id=int(tree)).get_descendants_and_self()
|
||||
except self.model.DoesNotExist:
|
||||
self.queryset = self.model.objects.none()
|
||||
elif root_tree:
|
||||
if root_tree.isnumeric():
|
||||
try:
|
||||
self.queryset = self.model.objects.get(id=int(root_tree)).get_root().get_descendants_and_self()
|
||||
except self.model.DoesNotExist:
|
||||
self.queryset = self.model.objects.none()
|
||||
|
||||
else:
|
||||
return self.annotate_recipe(queryset=super().get_queryset(), request=self.request,
|
||||
serializer=self.serializer_class, tree=True)
|
||||
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
|
||||
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
# only order if not root_tree or tree mde because in these modes the sorting is relevant for the client
|
||||
if not root_tree and not tree:
|
||||
self.queryset = self.queryset.order_by(Lower('name').asc())
|
||||
|
||||
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class,
|
||||
tree=True)
|
||||
@@ -1359,9 +1372,103 @@ class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
|
||||
return Response(self.serializer_class(qs, many=True).data)
|
||||
|
||||
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
|
||||
def batch_update(self, request):
|
||||
serializer = self.serializer_class(data=request.data, partial=True)
|
||||
|
||||
if serializer.is_valid():
|
||||
recipes = Recipe.objects.filter(id__in=serializer.validated_data['recipes'], space=self.request.space)
|
||||
safe_recipe_ids = Recipe.objects.filter(id__in=serializer.validated_data['recipes'], space=self.request.space).values_list('id', flat=True)
|
||||
|
||||
if 'keywords_add' in serializer.validated_data:
|
||||
keyword_relations = []
|
||||
for r in recipes:
|
||||
for k in serializer.validated_data['keywords_add']:
|
||||
keyword_relations.append(Recipe.keywords.through(recipe_id=r.pk, keyword_id=k))
|
||||
Recipe.keywords.through.objects.bulk_create(keyword_relations, ignore_conflicts=True, unique_fields=('recipe_id', 'keyword_id',))
|
||||
|
||||
if 'keywords_remove' in serializer.validated_data:
|
||||
for k in serializer.validated_data['keywords_remove']:
|
||||
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids, keyword_id=k).delete()
|
||||
|
||||
if 'keywords_set' in serializer.validated_data and len(serializer.validated_data['keywords_set']) > 0:
|
||||
keyword_relations = []
|
||||
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
|
||||
for r in recipes:
|
||||
for k in serializer.validated_data['keywords_set']:
|
||||
keyword_relations.append(Recipe.keywords.through(recipe_id=r.pk, keyword_id=k))
|
||||
Recipe.keywords.through.objects.bulk_create(keyword_relations, ignore_conflicts=True, unique_fields=('recipe_id', 'keyword_id',))
|
||||
|
||||
if 'keywords_remove_all' in serializer.validated_data and serializer.validated_data['keywords_remove_all']:
|
||||
Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
|
||||
|
||||
if 'working_time' in serializer.validated_data:
|
||||
recipes.update(working_time=serializer.validated_data['working_time'])
|
||||
|
||||
if 'waiting_time' in serializer.validated_data:
|
||||
recipes.update(waiting_time=serializer.validated_data['waiting_time'])
|
||||
|
||||
if 'servings' in serializer.validated_data:
|
||||
recipes.update(servings=serializer.validated_data['servings'])
|
||||
|
||||
if 'servings_text' in serializer.validated_data:
|
||||
recipes.update(servings_text=serializer.validated_data['servings_text'])
|
||||
|
||||
if 'private' in serializer.validated_data and serializer.validated_data['private'] is not None:
|
||||
recipes.update(private=serializer.validated_data['private'])
|
||||
|
||||
if 'shared_add' in serializer.validated_data:
|
||||
shared_relation = []
|
||||
for r in recipes:
|
||||
for u in serializer.validated_data['shared_add']:
|
||||
shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u))
|
||||
Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',))
|
||||
|
||||
if 'shared_remove' in serializer.validated_data:
|
||||
for s in serializer.validated_data['shared_remove']:
|
||||
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids, user_id=s).delete()
|
||||
|
||||
if 'shared_set' in serializer.validated_data and len(serializer.validated_data['shared_set']) > 0:
|
||||
shared_relation = []
|
||||
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
|
||||
for r in recipes:
|
||||
for u in serializer.validated_data['shared_set']:
|
||||
shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u))
|
||||
Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',))
|
||||
|
||||
if 'shared_remove_all' in serializer.validated_data and serializer.validated_data['shared_remove_all']:
|
||||
Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete()
|
||||
|
||||
if 'clear_description' in serializer.validated_data and serializer.validated_data['clear_description']:
|
||||
recipes.update(description='')
|
||||
|
||||
if 'show_ingredient_overview' in serializer.validated_data and serializer.validated_data['show_ingredient_overview'] is not None:
|
||||
recipes.update(show_ingredient_overview=serializer.validated_data['show_ingredient_overview'])
|
||||
|
||||
return Response({}, 200)
|
||||
|
||||
return Response(serializer.errors, 400)
|
||||
|
||||
@extend_schema(responses=RecipeSerializer(many=False))
|
||||
@decorators.action(detail=True, pagination_class=None, methods=['PATCH'], serializer_class=RecipeSerializer)
|
||||
def delete_external(self, request, pk):
|
||||
obj = self.get_object()
|
||||
if obj.get_space() != request.space and has_group_permission(request.user, ['user']):
|
||||
raise PermissionDenied(detail='You do not have the required permission to perform this action', code=403)
|
||||
|
||||
if obj.storage:
|
||||
get_recipe_provider(obj).delete_file(obj)
|
||||
obj.storage = None
|
||||
obj.file_path = ''
|
||||
obj.file_uid = ''
|
||||
obj.save()
|
||||
|
||||
return Response(self.serializer_class(obj, many=False, context={'request': request}).data)
|
||||
|
||||
|
||||
@extend_schema_view(list=extend_schema(
|
||||
parameters=[OpenApiParameter(name='food_id', description='ID of food to filter for', type=int), ]))
|
||||
parameters=[OpenApiParameter(name='food_id', description='ID of food to filter for', type=int),
|
||||
OpenApiParameter(name='query', description='query that looks into food, base unit or converted unit by name', type=str), ]))
|
||||
class UnitConversionViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
queryset = UnitConversion.objects
|
||||
serializer_class = UnitConversionSerializer
|
||||
@@ -1373,6 +1480,10 @@ class UnitConversionViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
if food_id is not None:
|
||||
self.queryset = self.queryset.filter(food_id=food_id)
|
||||
|
||||
query = self.request.query_params.get('query', None)
|
||||
if query is not None:
|
||||
self.queryset = self.queryset.filter(Q(food__name__icontains=query) | Q(base_unit__name__icontains=query) | Q(converted_unit__name__icontains=query))
|
||||
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
|
||||
|
||||
@@ -1654,7 +1765,8 @@ class AutomationViewSet(LoggingMixin, StandardFilterModelViewSet):
|
||||
|
||||
|
||||
@extend_schema_view(list=extend_schema(parameters=[
|
||||
OpenApiParameter(name='internal_note', description=_('Text field to store data that gets carried over to the UserSpace created from the InviteLink'), type=str)
|
||||
OpenApiParameter(name='internal_note', description=_('Text field to store data that gets carried over to the UserSpace created from the InviteLink'), type=str),
|
||||
OpenApiParameter(name='unused', description=_('Only return InviteLinks that have not been used yet.'), type=bool),
|
||||
]))
|
||||
class InviteLinkViewSet(LoggingMixin, StandardFilterModelViewSet):
|
||||
queryset = InviteLink.objects
|
||||
@@ -1667,6 +1779,10 @@ class InviteLinkViewSet(LoggingMixin, StandardFilterModelViewSet):
|
||||
if internal_note is not None:
|
||||
self.queryset = self.queryset.filter(internal_note=internal_note)
|
||||
|
||||
unused = self.request.query_params.get('unused', False)
|
||||
if unused:
|
||||
self.queryset = self.queryset.filter(used_by=None)
|
||||
|
||||
if is_space_owner(self.request.user, self.request.space):
|
||||
self.queryset = self.queryset.filter(space=self.request.space).all()
|
||||
return super().get_queryset()
|
||||
@@ -1789,9 +1905,9 @@ class RecipeUrlImportView(APIView):
|
||||
return Response(RecipeFromSourceResponseSerializer(context={'request': request}).to_representation(response), status=status.HTTP_200_OK)
|
||||
|
||||
tandoor_url = None
|
||||
if re.match('^(.)*/recipe/[0-9]+/?share=[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||
if re.match(r'^(.)*/recipe/[0-9]+/\?share=[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||
tandoor_url = url.replace('/recipe/', '/api/recipe/')
|
||||
elif re.match('^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||
elif re.match(r'^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||
tandoor_url = (url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/recipe/[0-9]+', url)[1], '') + '?share=' +
|
||||
re.split('/recipe/[0-9]+', url)[1].replace('/', ''))
|
||||
if tandoor_url and validate_import_url(tandoor_url):
|
||||
@@ -1886,6 +2002,12 @@ class AiImportView(APIView):
|
||||
|
||||
messages = []
|
||||
uploaded_file = serializer.validated_data['file']
|
||||
|
||||
if serializer.validated_data['recipe_id']:
|
||||
if recipe := Recipe.objects.filter(id=serializer.validated_data['recipe_id']).first():
|
||||
if recipe.file_path:
|
||||
uploaded_file = get_recipe_provider(recipe).get_file(recipe)
|
||||
|
||||
if uploaded_file:
|
||||
base64type = None
|
||||
try:
|
||||
@@ -2404,7 +2526,7 @@ def meal_plans_to_ical(queryset, filename):
|
||||
request=inline_serializer(name="IngredientStringSerializer", fields={'text': CharField()}),
|
||||
responses=inline_serializer(name="ParsedIngredientSerializer",
|
||||
fields={'amount': IntegerField(), 'unit': CharField(), 'food': CharField(),
|
||||
'note': CharField()})
|
||||
'note': CharField(), 'original_text': CharField()})
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
|
||||
@@ -2414,13 +2536,20 @@ def ingredient_from_string(request):
|
||||
ingredient_parser = IngredientParser(request, False)
|
||||
amount, unit, food, note = ingredient_parser.parse(text)
|
||||
|
||||
ingredient = {'amount': amount, 'unit': None, 'food': None, 'note': note}
|
||||
ingredient = {'amount': amount, 'unit': None, 'food': None, 'note': note, 'original_text': text}
|
||||
if food:
|
||||
food, created = Food.objects.get_or_create(space=request.space, name=food)
|
||||
ingredient['food'] = {'name': food.name, 'id': food.id}
|
||||
if food_obj := Food.objects.filter(space=request.space).filter(Q(name=food) | Q(plural_name=food)).first():
|
||||
ingredient['food'] = {'name': food_obj.name, 'id': food_obj.id}
|
||||
else:
|
||||
food_obj = Food.objects.create(space=request.space, name=food)
|
||||
ingredient['food'] = {'name': food_obj.name, 'id': food_obj.id}
|
||||
|
||||
if unit:
|
||||
unit, created = Unit.objects.get_or_create(space=request.space, name=unit)
|
||||
if unit_obj := Unit.objects.filter(space=request.space).filter(Q(name=unit) | Q(plural_name=unit)).first():
|
||||
ingredient['food'] = {'name': unit_obj.name, 'id': unit_obj.id}
|
||||
else:
|
||||
unit_obj = Unit.objects.create(space=request.space, name=unit)
|
||||
ingredient['food'] = {'name': unit_obj.name, 'id': unit_obj.id}
|
||||
ingredient['unit'] = {'name': unit.name, 'id': unit.id}
|
||||
|
||||
return JsonResponse(ingredient, status=200)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from datetime import datetime, timedelta
|
||||
from io import StringIO
|
||||
from uuid import UUID
|
||||
@@ -13,7 +14,7 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from django.core.cache import caches
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ValidationError, PermissionDenied, BadRequest
|
||||
from django.core.management import call_command
|
||||
from django.db import models
|
||||
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse
|
||||
@@ -238,11 +239,12 @@ def system(request):
|
||||
|
||||
for x in r.zrange('api:space-request-count', 0, 20, withscores=True, desc=True):
|
||||
s = x[0].decode('utf-8')
|
||||
space_stats = [Space.objects.get(pk=s).name, x[1]]
|
||||
for i in range(0, 6):
|
||||
d = (date.today() - timedelta(days=i)).isoformat()
|
||||
space_stats.append(r.zscore(f'api:space-request-count:{d}', s))
|
||||
api_space_stats.append(space_stats)
|
||||
if space := Space.objects.filter(pk=s).first():
|
||||
space_stats = [space.name, x[1]]
|
||||
for i in range(0, 6):
|
||||
d = (date.today() - timedelta(days=i)).isoformat()
|
||||
space_stats.append(r.zscore(f'api:space-request-count:{d}', s))
|
||||
api_space_stats.append(space_stats)
|
||||
|
||||
cache_response = caches['default'].get(f'system_view_test_cache_entry', None)
|
||||
if not cache_response:
|
||||
@@ -266,6 +268,22 @@ def system(request):
|
||||
})
|
||||
|
||||
|
||||
def plugin_update(request):
|
||||
if not request.user.is_superuser:
|
||||
raise PermissionDenied
|
||||
|
||||
if not 'module' in request.GET:
|
||||
raise BadRequest
|
||||
|
||||
for p in PLUGINS:
|
||||
if p['module'] == request.GET['module']:
|
||||
update_response = subprocess.check_output(['git', 'pull'], cwd=p['base_path'])
|
||||
print(update_response)
|
||||
return HttpResponseRedirect(reverse('view_system'))
|
||||
|
||||
return HttpResponseRedirect(reverse('view_system'))
|
||||
|
||||
|
||||
def setup(request):
|
||||
with scopes_disabled():
|
||||
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
|
||||
|
||||
@@ -11,34 +11,14 @@ If you like this application and want it to give back, there are many ways to co
|
||||
If you know any foreign languages you can:
|
||||
Improve the translations for any of the existing languages.
|
||||
|
||||
Add a new language to the long list of existing translations.
|
||||
|
||||
- Armenian
|
||||
- Bulgarian
|
||||
- Catalan
|
||||
- Czech
|
||||
- Danish
|
||||
- Dutch
|
||||
- English
|
||||
- French
|
||||
- German
|
||||
- Hungarian
|
||||
- Italian
|
||||
- Latvian
|
||||
- Norwegian
|
||||
- Polish
|
||||
- Russian
|
||||
- Spanish
|
||||
- Swedish
|
||||
|
||||
See [here](/docs/contribute/translations) for further information on how to contribute translation to Tandoor.
|
||||
See [here](/contribute/translations/) for further information on how to contribute translation to Tandoor.
|
||||
|
||||
## Issues and Feature Requests
|
||||
|
||||
The most basic but also very important way of contributing is reporting issues and commenting on ideas and feature requests
|
||||
The most basic but also crucial way of contributing is reporting issues and commenting on ideas and feature requests
|
||||
over at [GitHub issues](https://github.com/vabene1111/recipes/issues).
|
||||
|
||||
Without feedback improvement can't happen, so don't hesitate to say what you want to say.
|
||||
Without feedback, improvement can't happen, so don't hesitate to say what you want to say.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -46,12 +26,12 @@ Helping improve the documentation for Tandoor is one of the easiest ways to give
|
||||
You can write guides on how to install and configure Tandoor expanding our repository of non-standard configuations.
|
||||
Or you can write how-to guides using some of Tandoor's advanced features such as authentication or automation.
|
||||
|
||||
See [here](/docs/contribute/documentation) for more information on how to add documentation to Tandoor.
|
||||
See [here](/contribute/documentation/) for more information on how to add documentation to Tandoor.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
For the truly ambitious, you can help write code to fix issues, add additional features, or write your own scripts using
|
||||
Tandoor's extensive API and share your work with the community.
|
||||
|
||||
Before writing any code, please make sure that you review [contribution guidelines](/docs/contribute/guidelines) and
|
||||
[VSCode](/docs/contribute/vscode) or [PyCharm](/docs/contribute/pycharm) specific configurations.
|
||||
Before writing any code, please make sure that you review [contribution guidelines](/contribute/guidelines/) and
|
||||
[VSCode](/contribute/vscode) or [PyCharm](/contribute/pycharm) specific configurations.
|
||||
|
||||
@@ -32,10 +32,10 @@ To contribute to the project you are required to use the following packages with
|
||||
|
||||
## Testing
|
||||
|
||||
Django uses pytest-django to implement a full suite of testing. If you make any functional changes, please implment the appropriate
|
||||
Django uses pytest-django to implement a full suite of testing. If you make any functional changes, please implement the appropriate
|
||||
tests.
|
||||
|
||||
Tandoor is also actively soliciting contribors willing to setup vue3 testing. If you have knowledge in this area it would be greatly appreciated.
|
||||
Tandoor is also actively soliciting contributors willing to setup vue3 testing. If you have knowledge in this area it would be greatly appreciated.
|
||||
|
||||
## API Client
|
||||
|
||||
@@ -44,9 +44,7 @@ Tandoor is also actively soliciting contribors willing to setup vue3 testing. If
|
||||
The OpenAPI Generator is a Java project. You must have the java binary executable available on your PATH for this to work.
|
||||
|
||||
Tandoor uses [django-rest-framework](https://www.django-rest-framework.org/) for API implementation. Making contributions that impact the API requires an understanding of
|
||||
Viewsets and Serializers.
|
||||
|
||||
Also double check that your changes are actively reflected in the schema so that client apis are generated accurately.
|
||||
ViewSets and Serializers.
|
||||
|
||||
The API Client is generated automatically from the OpenAPI interface provided by the Django REST framework.
|
||||
For this [openapi-generator](https://github.com/OpenAPITools/openapi-generator) is used.
|
||||
@@ -55,17 +53,9 @@ Install it using your desired setup method. (For example, using `npm install @op
|
||||
|
||||
### Vue
|
||||
|
||||
Navigate to `vue/src/utils/openapi`.
|
||||
|
||||
Generate the schema using `openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.)
|
||||
|
||||
### Vue3
|
||||
|
||||
Navigate to `vue3/src/openapi`.
|
||||
|
||||
Generate the schema using `openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.)
|
||||
Generate the schema using the `generate_api_client.py` script in the main directory.
|
||||
|
||||
## Install and Configuration
|
||||
|
||||
Instructions for [VSCode](/docs/contribute/vscode)
|
||||
Instructions for [PyCharm](/docs/contribute/pycharm)
|
||||
Instructions for [VSCode](/contribute/vscode)
|
||||
Instructions for [PyCharm](/contribute/pycharm)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<!-- prettier-ignore-start -->
|
||||
!!! info "Development Setup"
|
||||
The dev setup is a little messy as this application combines the best (at least in my opinion) of both Django and Vue.js.
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### Devcontainer Setup
|
||||
|
||||
@@ -32,17 +30,15 @@ populated from default values.
|
||||
|
||||
### Vue.js
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
!!! warning "Feature Freeze"
|
||||
With the exception of bug fixes, no changes will be accepted on the legacy `vue` front-end.
|
||||
<!-- prettier-ignore-end -->
|
||||
!!! danger "Development Setup"
|
||||
The vite dev server **must** be started before the django runserver command is run or else django will **not** recognize it and try to fallback to the build files.
|
||||
|
||||
Most new frontend pages are build using [Vue.js](https://vuejs.org/).
|
||||
The frontend is build using [Vue.js](https://vuejs.org/).
|
||||
|
||||
In order to work on these pages, you will have to install a Javascript package manager of your choice. The following examples use yarn.
|
||||
|
||||
In the `vue` folder run `yarn install` followed by `yarn build` to install and build the legacy front-end.
|
||||
In the `vue3` folder run `yarn install` followed by `yarn build` to install and build the new front-end.
|
||||
1. go to the `vue3` and run `yarn install` to install the dependencies
|
||||
2. run `yarn serve` to start the dev server that allows hot reloading and easy and quick development
|
||||
|
||||
After that you can use `yarn serve` from the `vue3` folder to start the development server, and proceed to test your changes.
|
||||
If you do not wish to work on those pages, but instead want the application to work properly during development, run `yarn build` to build the frontend pages once.
|
||||
If you do not wish to work on those pages, but instead want the application to work properly during development, run `yarn build` to build the frontend pages once. After that you
|
||||
might need to run `python manage.py collectstatic` to setup the static files.
|
||||
|
||||
@@ -16,8 +16,6 @@ Maintained by [Aimo](https://github.com/aimok04/kitshn)
|
||||
- Website: [https://kitshn.app/](https://kitshn.app/)
|
||||
- Appstores: [Apple](https://apps.apple.com/us/app/kitshn-for-tandoor/id6740168361), [Android](https://play.google.com/store/apps/details?id=de.kitshn.android)
|
||||
|
||||
|
||||
|
||||
### Untare (discontinued)
|
||||
|
||||
Maintained by [phantomate](https://github.com/phantomate/Untare)
|
||||
|
||||
@@ -33,17 +33,26 @@ VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=esbenp.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! note
|
||||
In order to debug vue yarn and vite servers must be started before starting the django server.
|
||||
In order to hot reload vue, the `yarn dev` server must be started before starting the django server.
|
||||
|
||||
There are a number of built in tasks that are available. Here are a few of the key ones:
|
||||
|
||||
- `Setup Dev Server` - Runs all the prerequisite steps so that the dev server can be run inside VSCode.
|
||||
- `Setup Tests` - Runs all prerequisites so tests can be run inside VSCode.
|
||||
|
||||
Once these are run, you should be able to run/debug a django server in VSCode as well as run/debug tests directly through VSCode.
|
||||
There are also a few other tasks specified in case you have specific development needs:
|
||||
Once these are run, there are 2 options. If you want to run a vue3 server in a hot reload mode for quick development of the frontend, you should run a development vue server:
|
||||
|
||||
- `Yarn Dev` - Runs development Vue.js vite server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime.
|
||||
|
||||
If not, you need to build and copy the frontend to the django server. If you make changes to the frontend, you need to re-run this and restart the django server:
|
||||
|
||||
- `Collect Static Files` - Builds and collects the vue3 frontend so that it can be served via the django server.
|
||||
|
||||
Once either of those steps are done, you can start the django server:
|
||||
|
||||
- `Run Dev Server` - Runs a django development server not connected to VSCode.
|
||||
|
||||
There are also a few other tasks specified in case you have specific development needs:
|
||||
|
||||
- `Run all pytests` - Runs all the pytests outside of VSCode.
|
||||
- `Yarn Serve` - Runs development Vue.js server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime.
|
||||
- `Serve Documentation` - Runs a documentation server. Useful if you want to see how changes to documentation show up.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<a href="https://docs.tandoor.dev/install/docker/" target="_blank" rel="noopener noreferrer">Installation</a> •
|
||||
<a href="https://docs.tandoor.dev/" target="_blank" rel="noopener noreferrer">Docs</a> •
|
||||
<a href="https://app.tandoor.dev/accounts/login/?demo" target="_blank" rel="noopener noreferrer">Demo</a> •
|
||||
<a href="https://community.tandoor.dev" target="_blank" rel="noopener noreferrer">Community</a> •
|
||||
<a href="https://discord.gg/RhzBrfWgtp" target="_blank" rel="noopener noreferrer">Discord</a>
|
||||
</p>
|
||||
|
||||
@@ -65,13 +66,13 @@ Share some information on how you use Tandoor to help me improve the application
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://discord.gg/RhzBrfWgtp">Discord</a></td>
|
||||
<td>We have a public Discord server that anyone can join. This is where all our developers and contributors hang out and where we make announcements</td>
|
||||
<td><a href="https://community.tandoor.dev">Community</a></td>
|
||||
<td>Get support, share best practices, discuss feature ideas, and meet other Tandoor users.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="https://twitter.com/TandoorRecipes">Twitter</a></td>
|
||||
<td>You can follow our Twitter account to get updates on new features or releases</td>
|
||||
<td><a href="https://discord.gg/RhzBrfWgtp">Discord</a></td>
|
||||
<td>We have a public Discord server that anyone can join. This is where all our developers and contributors hang out and where we make announcements</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ spec:
|
||||
source venv/bin/activate
|
||||
echo "Updating database"
|
||||
python manage.py migrate
|
||||
python manage.py collectstatic_js_reverse
|
||||
python manage.py collectstatic --noinput
|
||||
echo "Setting media file attributes"
|
||||
chown -R 65534:65534 /opt/recipes/mediafiles
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## K8s Setup
|
||||
|
||||
This is a setup which should be sufficient for production use. Be sure to replace the default secrets!
|
||||
This is a setup which should be sufficient for production use. Be sure to replace the default secrets! You can find the example files [here](https://github.com/MyDigitalLife/recipes/tree/fix-k8s-documentation/docs/install/k8s) on Github.
|
||||
|
||||
## Files
|
||||
|
||||
|
||||
@@ -7,20 +7,19 @@ server {
|
||||
|
||||
# serve media files
|
||||
location /media {
|
||||
alias /opt/recipes/mediafiles;
|
||||
alias ${MEDIA_ROOT};
|
||||
add_header Content-Disposition 'attachment; filename="$args"';
|
||||
}
|
||||
|
||||
# serve service worker under main path
|
||||
location = /service-worker.js {
|
||||
alias /opt/recipes/staticfiles/vue3/service-worker.js;
|
||||
alias ${STATIC_ROOT}/vue3/service-worker.js;
|
||||
}
|
||||
|
||||
# pass requests for dynamic content to gunicorn
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_pass http://localhost:8080;
|
||||
|
||||
proxy_pass http://localhost:${TANDOOR_PORT};
|
||||
error_page 502 /errors/http502.html;
|
||||
}
|
||||
|
||||
21
plugin.py
Normal file
21
plugin.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
#TODO clean existing links for when plugins are uninstalled or not necessary because it will just be empty links?
|
||||
|
||||
PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins')
|
||||
if os.path.isdir(PLUGINS_DIRECTORY):
|
||||
for d in os.listdir(PLUGINS_DIRECTORY):
|
||||
if d != '__pycache__':
|
||||
try:
|
||||
subprocess.run(['python', 'setup_repo.py'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'recipes', 'plugins', d))
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
print(f'ERROR failed to link plugin {d}')
|
||||
|
||||
subprocess.run(['npm', 'install', '--global', 'yarn'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))
|
||||
subprocess.run(['yarn', 'install'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))
|
||||
subprocess.run(['yarn', 'build'], shell=(os.name == 'nt'), cwd=os.path.join(BASE_DIR, 'vue3'))
|
||||
@@ -221,10 +221,7 @@ try:
|
||||
'module': f'recipes.plugins.{d}',
|
||||
'base_path': os.path.join(BASE_DIR, 'recipes', 'plugins', d),
|
||||
'base_url': plugin_class.base_url,
|
||||
'bundle_name': plugin_class.bundle_name if hasattr(plugin_class, 'bundle_name') else '',
|
||||
'api_router_name': plugin_class.api_router_name if hasattr(plugin_class, 'api_router_name') else '',
|
||||
'nav_main': plugin_class.nav_main if hasattr(plugin_class, 'nav_main') else '',
|
||||
'nav_dropdown': plugin_class.nav_dropdown if hasattr(plugin_class, 'nav_dropdown') else '',
|
||||
}
|
||||
PLUGINS.append(plugin_config)
|
||||
print(f'PLUGIN {d} loaded')
|
||||
@@ -534,28 +531,6 @@ if REDIS_HOST:
|
||||
# Vue webpack settings
|
||||
VUE_DIR = os.path.join(BASE_DIR, 'vue')
|
||||
|
||||
WEBPACK_LOADER = {
|
||||
'DEFAULT': {
|
||||
'CACHE': not DEBUG,
|
||||
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
|
||||
'STATS_FILE': os.path.join(VUE_DIR, 'webpack-stats.json'),
|
||||
'POLL_INTERVAL': 0.1,
|
||||
'TIMEOUT': None,
|
||||
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
|
||||
},
|
||||
}
|
||||
|
||||
for p in PLUGINS:
|
||||
if p['bundle_name'] != '':
|
||||
WEBPACK_LOADER[p['bundle_name']] = {
|
||||
'CACHE': not DEBUG,
|
||||
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
|
||||
'STATS_FILE': os.path.join(p["base_path"], 'vue', 'webpack-stats.json'),
|
||||
'POLL_INTERVAL': 0.1,
|
||||
'TIMEOUT': None,
|
||||
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
|
||||
}
|
||||
|
||||
DJANGO_VITE = {
|
||||
"default": {
|
||||
"dev_mode": False,
|
||||
|
||||
@@ -14,7 +14,7 @@ bleach==6.2.0
|
||||
gunicorn==23.0.0
|
||||
lxml==5.3.1
|
||||
Markdown==3.7
|
||||
Pillow==11.1.0
|
||||
Pillow==11.3.0
|
||||
psycopg2-binary==2.9.10
|
||||
python-dotenv==1.0.0
|
||||
requests==2.32.4
|
||||
@@ -26,10 +26,10 @@ pyyaml==6.0.2
|
||||
uritemplate==4.1.1
|
||||
beautifulsoup4==4.12.3
|
||||
microdata==0.8.0
|
||||
mock==5.1.0
|
||||
mock==5.2.0
|
||||
Jinja2==3.1.6
|
||||
django-allauth[mfa,socialaccount]==65.9.0
|
||||
recipe-scrapers==15.6.0
|
||||
recipe-scrapers==15.8.0
|
||||
django-scopes==2.0.0
|
||||
django-treebeard==4.7.1
|
||||
django-cors-headers==4.6.0
|
||||
@@ -41,9 +41,9 @@ python-ldap==3.4.4
|
||||
django-auth-ldap==4.6.0
|
||||
pyppeteer==2.0.0
|
||||
pytubefix==9.2.2
|
||||
aiohttp==3.12.14
|
||||
aiohttp==3.12.15
|
||||
inflection==0.5.1
|
||||
redis==5.2.1
|
||||
redis==6.2.0
|
||||
hiredis==3.2.1
|
||||
requests-oauthlib==2.0.0
|
||||
pyjwt==2.10.1
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/luxon": "^3.6.2",
|
||||
"@types/luxon": "^3.7.1",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@vueform/multiselect": "^2.6.11",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"@vueuse/router": "^13.1.0",
|
||||
"luxon": "^3.6.1",
|
||||
"@vueuse/core": "^13.6.0",
|
||||
"@vueuse/router": "^13.6.0",
|
||||
"luxon": "^3.7.1",
|
||||
"mavon-editor": "^3.0.1",
|
||||
"pinia": "^3.0.2",
|
||||
"vue": "^3.5.13",
|
||||
@@ -22,8 +23,7 @@
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-simple-calendar": "7.1.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"vuetify": "^3.9.0"
|
||||
"vuetify": "^3.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
@@ -32,20 +32,21 @@
|
||||
"@types/node": "^24.0.8",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"esbuild-register": "^3.6.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "6.3.5",
|
||||
"vite-plugin-pwa": "^1.0.1",
|
||||
"workbox-core": "^7.3.0",
|
||||
"workbox-build": "^7.3.0",
|
||||
"workbox-window": "^7.3.0",
|
||||
"vite-plugin-pwa": "^1.0.2",
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vue-tsc": "^2.2.8",
|
||||
"workbox-background-sync": "^7.3.0",
|
||||
"workbox-build": "^7.3.0",
|
||||
"workbox-core": "^7.3.0",
|
||||
"workbox-expiration": "^7.3.0",
|
||||
"workbox-navigation-preload": "^7.3.0",
|
||||
"workbox-precaching": "^7.3.0",
|
||||
"workbox-routing": "^7.3.0",
|
||||
"workbox-strategies": "^7.3.0",
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vue-tsc": "^2.2.8"
|
||||
"workbox-window": "^7.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,15 +137,21 @@ import MessageListDialog from "@/components/dialogs/MessageListDialog.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import NavigationDrawerContextMenu from "@/components/display/NavigationDrawerContextMenu.vue";
|
||||
import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||
import {onMounted} from "vue";
|
||||
import {nextTick, onMounted} from "vue";
|
||||
import {isSpaceAboveLimit} from "@/utils/logic_utils";
|
||||
import {useMediaQuery} from "@vueuse/core";
|
||||
import {useMediaQuery, useTitle} from "@vueuse/core";
|
||||
import HelpDialog from "@/components/dialogs/HelpDialog.vue";
|
||||
import {NAVIGATION_DRAWER} from "@/utils/navigation.ts";
|
||||
import {useNavigation} from "@/composables/useNavigation.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const {lgAndUp} = useDisplay()
|
||||
const {getDjangoUrl} = useDjangoUrls()
|
||||
const {t} = useI18n()
|
||||
|
||||
const title = useTitle()
|
||||
const router = useRouter()
|
||||
|
||||
const isPrintMode = useMediaQuery('print')
|
||||
|
||||
@@ -153,6 +159,19 @@ onMounted(() => {
|
||||
useUserPreferenceStore()
|
||||
})
|
||||
|
||||
/**
|
||||
* global title update handler, might be overridden by page specific handlers
|
||||
*/
|
||||
router.afterEach((to, from) => {
|
||||
nextTick(() => {
|
||||
if (to.meta.title) {
|
||||
title.value = t(to.meta.title)
|
||||
} else {
|
||||
title.value = 'Tandoor'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -8,54 +8,54 @@ import vuetify from "@/vuetify";
|
||||
import mavonEditor from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import 'vite/modulepreload-polyfill';
|
||||
import { createRulesPlugin } from 'vuetify/labs/rules'
|
||||
import {createRulesPlugin} from 'vuetify/labs/rules'
|
||||
|
||||
import {setupI18n} from "@/i18n";
|
||||
import MealPlanPage from "@/pages/MealPlanPage.vue";
|
||||
import {TANDOOR_PLUGINS, TandoorPlugin} from "@/types/Plugins.ts";
|
||||
|
||||
let routes = [
|
||||
{path: '/', component: () => import("@/pages/StartPage.vue"), name: 'StartPage'},
|
||||
{path: '/', component: () => import("@/pages/StartPage.vue"), name: 'StartPage' },
|
||||
{path: '/search', redirect: {name: 'StartPage'}},
|
||||
{path: '/test', component: () => import("@/pages/TestPage.vue"), name: 'view_test'},
|
||||
{path: '/help', component: () => import("@/pages/HelpPage.vue"), name: 'HelpPage'},
|
||||
{path: '/help', component: () => import("@/pages/HelpPage.vue"), name: 'HelpPage', meta: {title: 'Help'}},
|
||||
{
|
||||
path: '/settings', component: () => import("@/pages/SettingsPage.vue"), name: 'SettingsPage', redirect: '/settings/account',
|
||||
children: [
|
||||
{path: 'account', component: () => import("@/components/settings/AccountSettings.vue"), name: 'AccountSettings'},
|
||||
{path: 'cosmetic', component: () => import("@/components/settings/CosmeticSettings.vue"), name: 'CosmeticSettings'},
|
||||
{path: 'shopping', component: () => import("@/components/settings/ShoppingSettings.vue"), name: 'ShoppingSettings'},
|
||||
{path: 'meal-plan', component: () => import("@/components/settings/MealPlanSettings.vue"), name: 'MealPlanSettings'},
|
||||
{path: 'search', component: () => import("@/components/settings/SearchSettings.vue"), name: 'SearchSettings'},
|
||||
{path: 'space', component: () => import("@/components/settings/SpaceSettings.vue"), name: 'SpaceSettings'},
|
||||
{path: 'space-members', component: () => import("@/components/settings/SpaceMemberSettings.vue"), name: 'SpaceMemberSettings'},
|
||||
{path: 'user-space', component: () => import("@/components/settings/UserSpaceSettings.vue"), name: 'UserSpaceSettings'},
|
||||
{path: 'open-data-import', component: () => import("@/components/settings/OpenDataImportSettings.vue"), name: 'OpenDataImportSettings'},
|
||||
{path: 'export', component: () => import("@/components/settings/ExportDataSettings.vue"), name: 'ExportDataSettings'},
|
||||
{path: 'api', component: () => import("@/components/settings/ApiSettings.vue"), name: 'ApiSettings'},
|
||||
]
|
||||
{path: 'account', component: () => import("@/components/settings/AccountSettings.vue"), name: 'AccountSettings', meta: {title: 'Settings'}},
|
||||
{path: 'cosmetic', component: () => import("@/components/settings/CosmeticSettings.vue"), name: 'CosmeticSettings', meta: {title: 'Settings'}},
|
||||
{path: 'shopping', component: () => import("@/components/settings/ShoppingSettings.vue"), name: 'ShoppingSettings', meta: {title: 'Settings'}},
|
||||
{path: 'meal-plan', component: () => import("@/components/settings/MealPlanSettings.vue"), name: 'MealPlanSettings', meta: {title: 'Settings'}},
|
||||
{path: 'search', component: () => import("@/components/settings/SearchSettings.vue"), name: 'SearchSettings', meta: {title: 'Settings'}},
|
||||
{path: 'space', component: () => import("@/components/settings/SpaceSettings.vue"), name: 'SpaceSettings', meta: {title: 'Settings'}},
|
||||
{path: 'space-members', component: () => import("@/components/settings/SpaceMemberSettings.vue"), name: 'SpaceMemberSettings', meta: {title: 'Settings'}},
|
||||
{path: 'user-space', component: () => import("@/components/settings/UserSpaceSettings.vue"), name: 'UserSpaceSettings', meta: {title: 'Settings'}},
|
||||
{path: 'open-data-import', component: () => import("@/components/settings/OpenDataImportSettings.vue"), name: 'OpenDataImportSettings', meta: {title: 'Settings'}},
|
||||
{path: 'export', component: () => import("@/components/settings/ExportDataSettings.vue"), name: 'ExportDataSettings', meta: {title: 'Settings'}},
|
||||
{path: 'api', component: () => import("@/components/settings/ApiSettings.vue"), name: 'ApiSettings', meta: {title: 'Settings'}},
|
||||
], meta: {title: 'Settings'}
|
||||
},
|
||||
//{path: '/settings/:page', component: SettingsPage, name: 'view_settings_page', props: true},
|
||||
{path: '/advanced-search', component: () => import("@/pages/SearchPage.vue"), name: 'SearchPage'},
|
||||
{path: '/shopping', component: () => import("@/pages/ShoppingListPage.vue"), name: 'ShoppingListPage'},
|
||||
{path: '/mealplan', component: MealPlanPage, name: 'MealPlanPage'},
|
||||
{path: '/books', component: () => import("@/pages/BooksPage.vue"), name: 'BooksPage'},
|
||||
{path: '/book/:bookId', component: () => import("@/pages/BookViewPage.vue"), name: 'BookViewPage', props: true},
|
||||
{path: '/recipe/import', component: () => import("@/pages/RecipeImportPage.vue"), name: 'RecipeImportPage'},
|
||||
{path: '/advanced-search', component: () => import("@/pages/SearchPage.vue"), name: 'SearchPage', meta: {title: 'Search'}},
|
||||
{path: '/shopping', component: () => import("@/pages/ShoppingListPage.vue"), name: 'ShoppingListPage', meta: {title: 'Shopping_list'}},
|
||||
{path: '/mealplan', component: MealPlanPage, name: 'MealPlanPage', meta: {title: 'Meal_Plan'}},
|
||||
{path: '/books', component: () => import("@/pages/BooksPage.vue"), name: 'BooksPage', meta: {title: 'Books'}},
|
||||
{path: '/book/:bookId', component: () => import("@/pages/BookViewPage.vue"), name: 'BookViewPage', props: true, meta: {title: 'Book'}},
|
||||
{path: '/recipe/import', component: () => import("@/pages/RecipeImportPage.vue"), name: 'RecipeImportPage', meta: {title: 'Import'}},
|
||||
|
||||
{path: '/recipe/:id', component: () => import("@/pages/RecipeViewPage.vue"), name: 'RecipeViewPage', props: true},
|
||||
{path: '/recipe/:id', component: () => import("@/pages/RecipeViewPage.vue"), name: 'RecipeViewPage', props: true, meta: {title: 'Recipe'}},
|
||||
{path: '/view/recipe/:id', redirect: {name: 'RecipeViewPage'}}, // old Tandoor v1 url pattern
|
||||
|
||||
{path: '/list/:model?', component: () => import("@/pages/ModelListPage.vue"), props: true, name: 'ModelListPage'},
|
||||
{path: '/edit/:model/:id?', component: () => import("@/pages/ModelEditPage.vue"), props: true, name: 'ModelEditPage'},
|
||||
{path: '/database', component: () => import("@/pages/DatabasePage.vue"), props: true, name: 'DatabasePage'},
|
||||
{path: '/database', component: () => import("@/pages/DatabasePage.vue"), props: true, name: 'DatabasePage', meta: {title: 'Database'}},
|
||||
|
||||
{path: '/ingredient-editor', component: () => import("@/pages/IngredientEditorPage.vue"), name: 'IngredientEditorPage'},
|
||||
{path: '/property-editor', component: () => import("@/pages/PropertyEditorPage.vue"), name: 'PropertyEditorPage'},
|
||||
{path: '/ingredient-editor', component: () => import("@/pages/IngredientEditorPage.vue"), name: 'IngredientEditorPage', meta: {title: 'Ingredient Editor'}},
|
||||
{path: '/property-editor', component: () => import("@/pages/PropertyEditorPage.vue"), name: 'PropertyEditorPage', meta: {title: 'Property_Editor'}},
|
||||
|
||||
{path: '/space-setup', component: () => import("@/pages/SpaceSetupPage.vue"), name: 'SpaceSetupPage'},
|
||||
|
||||
{path: '/:pathMatch(.*)*', component: () => import("@/pages/404Page.vue"), name: '404Page'},
|
||||
{path: '/:pathMatch(.*)*', component: () => import("@/pages/404Page.vue"), name: '404Page', meta: {title: 'NotFound'}},
|
||||
]
|
||||
|
||||
// load plugin routes into routing table
|
||||
@@ -63,8 +63,12 @@ TANDOOR_PLUGINS.forEach(plugin => {
|
||||
routes = routes.concat(plugin.routes)
|
||||
})
|
||||
|
||||
const basePath = localStorage.getItem("BASE_PATH")
|
||||
const pathname = basePath?.startsWith("http") ? new URL(basePath).pathname : undefined
|
||||
const base = pathname === "/" ? undefined : pathname
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
history: createWebHistory(base),
|
||||
routes,
|
||||
})
|
||||
|
||||
@@ -74,7 +78,7 @@ const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(vuetify)
|
||||
app.use(createRulesPlugin({ /* options */ }, vuetify.locale))
|
||||
app.use(createRulesPlugin({ /* options */}, vuetify.locale))
|
||||
app.use(router)
|
||||
app.use(i18n)
|
||||
app.use(mavonEditor) // TODO only use on pages that need it
|
||||
|
||||
114
vue3/src/components/dialogs/AutoPlanDialog.vue
Normal file
114
vue3/src/components/dialogs/AutoPlanDialog.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<v-dialog max-width="600px" :activator="props.activator" v-model="dialog">
|
||||
<v-card :loading="loading">
|
||||
<v-closable-card-title v-model="dialog" :title="$t('Auto_Planner')" icon="fa-solid fa-calendar-plus"></v-closable-card-title>
|
||||
|
||||
<v-card-text>
|
||||
|
||||
<v-form>
|
||||
<model-select model="MealType" v-model="autoMealPlan.mealTypeId" :object="false"></model-select>
|
||||
<model-select model="Keyword" v-model="autoMealPlan.keywordIds" mode="tags" :object="false"></model-select>
|
||||
|
||||
<v-number-input :label="$t('Servings')" v-model="autoMealPlan.servings"></v-number-input>
|
||||
|
||||
<v-date-input :label="$t('Date')"
|
||||
multiple="range"
|
||||
v-model="dateRangeValue"
|
||||
:first-day-of-week="useUserPreferenceStore().deviceSettings.mealplan_startingDayOfWeek"
|
||||
:show-week="useUserPreferenceStore().deviceSettings.mealplan_displayWeekNumbers"
|
||||
prepend-icon=""
|
||||
prepend-inner-icon="$calendar"
|
||||
></v-date-input>
|
||||
|
||||
<model-select model="User" v-model="autoMealPlan.shared" mode="tags"></model-select>
|
||||
<v-checkbox v-model="autoMealPlan.addshopping" :label="$t('AddToShopping')" hide-details></v-checkbox>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn @click="dialog = false">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn color="create" prepend-icon="fa-solid fa-person-running" @click="doAutoPlan()" :loading="loading">{{ $t('Create') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {useI18n} from "vue-i18n";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import {ApiApi, AutoMealPlan} from "@/openapi";
|
||||
import {onMounted, ref} from "vue";
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import {VDateInput} from 'vuetify/labs/VDateInput'
|
||||
import {DateTime} from "luxon";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore.ts";
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const props = defineProps({
|
||||
activator: {type: String, default: 'parent'},
|
||||
})
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const dialog = defineModel<boolean>({default: false})
|
||||
const loading = ref(false)
|
||||
|
||||
const dateRangeValue = ref([] as Date[])
|
||||
const autoMealPlan = ref({} as AutoMealPlan)
|
||||
|
||||
onMounted(() => {
|
||||
initializeRequest()
|
||||
})
|
||||
|
||||
/**
|
||||
* load default values for auto plan creation
|
||||
*/
|
||||
function initializeRequest() {
|
||||
autoMealPlan.value = {
|
||||
servings: 1,
|
||||
startDate: DateTime.now().toJSDate(),
|
||||
endDate: DateTime.now().plus({day: 7}).toJSDate(),
|
||||
shared: useUserPreferenceStore().userSettings.planShare,
|
||||
addshopping: useUserPreferenceStore().userSettings.mealplanAutoaddShopping,
|
||||
} as AutoMealPlan
|
||||
|
||||
dateRangeValue.value = []
|
||||
let currentDate = DateTime.fromJSDate(autoMealPlan.value.startDate).plus({day: 1}).toJSDate()
|
||||
while (currentDate <= autoMealPlan.value.endDate) {
|
||||
dateRangeValue.value.push(currentDate)
|
||||
currentDate = DateTime.fromJSDate(currentDate).plus({day: 1}).toJSDate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* perform auto plan creation
|
||||
*/
|
||||
function doAutoPlan() {
|
||||
let api = new ApiApi()
|
||||
loading.value = true
|
||||
|
||||
autoMealPlan.value.startDate = dateRangeValue.value[0]
|
||||
autoMealPlan.value.endDate = dateRangeValue.value[dateRangeValue.value.length - 1]
|
||||
console.log('requesting auto plan from ', autoMealPlan.value.startDate, ' to ', autoMealPlan.value.endDate)
|
||||
|
||||
api.apiAutoPlanCreate({autoMealPlan: autoMealPlan.value}).then(r => {
|
||||
dialog.value = false
|
||||
useMealPlanStore().refreshLastUpdatedPeriod()
|
||||
initializeRequest()
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS)
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
98
vue3/src/components/dialogs/BatchDeleteDialog.vue
Normal file
98
vue3/src/components/dialogs/BatchDeleteDialog.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<v-dialog max-width="600px" :activator="props.activator" v-model="dialog">
|
||||
<v-card :loading="loading">
|
||||
<v-closable-card-title
|
||||
:title="$t('delete_title', {type: $t(genericModel.model.localizationKey)})"
|
||||
:sub-title="genericModel.getLabel(props.source)"
|
||||
:icon="genericModel.model.icon"
|
||||
v-model="dialog"
|
||||
></v-closable-card-title>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
{{ $t('BatchDeleteConfirm') }}
|
||||
|
||||
<v-list>
|
||||
<v-list-item border v-for="item in itemsToDelete">
|
||||
{{ genericModel.getLabel(item) }}
|
||||
<template #append>
|
||||
<v-icon icon="fa-solid fa-xmark" color="error" variant="tonal" v-if="failedItems.includes(item)"></v-icon>
|
||||
<v-icon icon="fa-solid fa-check" color="success" variant="tonal" v-else-if="updatedItems.includes(item)"></v-icon>
|
||||
<v-icon icon="fa-solid fa-circle-notch fa-spin" variant="tonal" color="info" v-else-if="loading"></v-icon>
|
||||
|
||||
<v-btn icon="fa-solid fa-up-right-from-square" :to="{name: 'IngredientEditorPage', query: {food_id: item.id}}"
|
||||
v-if="genericModel.model.name == 'Food' && failedItems.includes(item)" size="small"></v-btn>
|
||||
<v-btn icon="fa-solid fa-up-right-from-square" :to="{name: 'IngredientEditorPage', query: {unit_id: item.id}}"
|
||||
v-if="genericModel.model.name == 'Unit' && failedItems.includes(item)" size="small"></v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<p class="font-italic text-disabled">{{$t('BatchDeleteHelp')}}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn color="error" @click="deleteAll()" :loading="loading">{{ $t('Delete_All') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref, watch} from "vue";
|
||||
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models.ts";
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const props = defineProps({
|
||||
model: {type: String as PropType<EditorSupportedModels>, required: true},
|
||||
items: {type: Array as PropType<Array<EditorSupportedTypes>>, required: true},
|
||||
activator: {type: String, default: 'parent'},
|
||||
})
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const dialog = defineModel<boolean>({default: false})
|
||||
const loading = ref(false)
|
||||
|
||||
const genericModel = getGenericModelFromString(props.model, t)
|
||||
|
||||
const itemsToDelete = ref<EditorSupportedTypes[]>([])
|
||||
const failedItems = ref<EditorSupportedTypes[]>([])
|
||||
const updatedItems = ref<EditorSupportedTypes[]>([])
|
||||
|
||||
watch(dialog, (newValue, oldValue) => {
|
||||
if(!oldValue && newValue){
|
||||
itemsToDelete.value = JSON.parse(JSON.stringify(props.items))
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* loop through the items and delete them
|
||||
*/
|
||||
function deleteAll() {
|
||||
let promises: Promise<any>[] = []
|
||||
loading.value = true
|
||||
|
||||
itemsToDelete.value.forEach(item => {
|
||||
promises.push(genericModel.destroy(item.id!).then((r: any) => {
|
||||
updatedItems.value.push(item)
|
||||
}).catch((err: any) => {
|
||||
failedItems.value.push(item)
|
||||
}))
|
||||
})
|
||||
|
||||
Promise.allSettled(promises).then(() => {
|
||||
loading.value = false
|
||||
emit('change')
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
166
vue3/src/components/dialogs/BatchEditRecipeDialog.vue
Normal file
166
vue3/src/components/dialogs/BatchEditRecipeDialog.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<v-dialog max-width="1200px" :activator="props.activator" v-model="dialog">
|
||||
<v-card :loading="loading">
|
||||
<v-closable-card-title
|
||||
:title="$t('BatchEdit')"
|
||||
:sub-title="$t('BatchEditUpdatingItemsCount', {type: $t('Recipes'), count: updateItems.length})"
|
||||
:icon="TRecipe.icon"
|
||||
v-model="dialog"
|
||||
></v-closable-card-title>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
|
||||
<v-form>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card :title="$t('Keywords')" :prepend-icon="TKeyword.icon" variant="plain">
|
||||
<v-card-text>
|
||||
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsAdd" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-add"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsRemove" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-minus"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<model-select model="Keyword" v-model="batchUpdateRequest.recipeBatchUpdate.keywordsSet" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-equals"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<v-checkbox :label="$t('RemoveAllType', {type: $t('Keywords')})" hide-details v-model="batchUpdateRequest.recipeBatchUpdate.keywordsRemoveAll"></v-checkbox>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card :title="$t('Private_Recipe')" :subtitle="$t('Private_Recipe_Help')" prepend-icon="fa-solid fa-eye-slash" variant="plain">
|
||||
<v-card-text>
|
||||
|
||||
<v-select :items="boolUpdateOptions" :label="$t('Private_Recipe')" clearable v-model="batchUpdateRequest.recipeBatchUpdate._private"></v-select>
|
||||
|
||||
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedAdd" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-add"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedRemove" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-minus"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<model-select model="User" v-model="batchUpdateRequest.recipeBatchUpdate.sharedSet" :object="false" allow-create mode="tags">
|
||||
<template #prepend>
|
||||
<v-icon icon="fa-solid fa-equals"></v-icon>
|
||||
</template>
|
||||
</model-select>
|
||||
<v-checkbox :label="$t('RemoveAllType', {type: $t('Users')})" hide-details v-model="batchUpdateRequest.recipeBatchUpdate.sharedRemoveAll"></v-checkbox>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card :title="$t('Miscellaneous')" prepend-icon="fa-solid fa-list" variant="plain">
|
||||
<v-card-text>
|
||||
<v-number-input :label="$t('WorkingTime')" v-model="batchUpdateRequest.recipeBatchUpdate.workingTime" :step="5">
|
||||
|
||||
</v-number-input>
|
||||
<v-number-input :label="$t('WaitingTime')" v-model="batchUpdateRequest.recipeBatchUpdate.waitingTime" :step="5">
|
||||
|
||||
</v-number-input>
|
||||
<v-number-input :label="$t('Serving')" v-model="batchUpdateRequest.recipeBatchUpdate.servings">
|
||||
|
||||
</v-number-input>
|
||||
<v-text-field :label="$t('ServingsText')" v-model="batchUpdateRequest.recipeBatchUpdate.servingsText" @update:model-value="updateServings = true">
|
||||
<template #append>
|
||||
<v-checkbox v-model="updateServings" hide-details></v-checkbox>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-select :items="boolUpdateOptions" :label="$t('show_ingredient_overview')" clearable v-model="batchUpdateRequest.recipeBatchUpdate.showIngredientOverview"></v-select>
|
||||
<v-checkbox hide-details :label="$t('DeleteSomething', {item: $t('Description')})" v-model="batchUpdateRequest.recipeBatchUpdate.clearDescription"></v-checkbox>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn color="warning" :loading="loading" @click="batchUpdateRecipes()" :disabled="updateItems.length < 1">{{ $t('Update') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref, watch} from "vue";
|
||||
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString, TKeyword, TRecipe} from "@/types/Models.ts";
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {ApiApi, ApiRecipeBatchUpdateUpdateRequest, Recipe, RecipeOverview} from "@/openapi";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const props = defineProps({
|
||||
items: {type: Array as PropType<Array<RecipeOverview>>, required: true},
|
||||
activator: {type: String, default: 'parent'},
|
||||
})
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const dialog = defineModel<boolean>({default: false})
|
||||
const loading = ref(false)
|
||||
|
||||
const updateItems = ref([] as RecipeOverview[])
|
||||
const batchUpdateRequest = ref({recipeBatchUpdate: {servingsText: ''}} as ApiRecipeBatchUpdateUpdateRequest)
|
||||
|
||||
const updateServings = ref(false)
|
||||
|
||||
const boolUpdateOptions = ref([
|
||||
{value: true, title: t('Yes')},
|
||||
{value: false, title: t('No')},
|
||||
])
|
||||
|
||||
/**
|
||||
* copy prop when dialog opens so that items remain when parent is updated after change is emitted
|
||||
*/
|
||||
watch(dialog, (newValue, oldValue) => {
|
||||
if (!oldValue && newValue && props.items != undefined) {
|
||||
batchUpdateRequest.value.recipeBatchUpdate.recipes = props.items.flatMap(r => r.id!)
|
||||
updateItems.value = JSON.parse(JSON.stringify(props.items))
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* perform batch request to update recipes
|
||||
*/
|
||||
function batchUpdateRecipes() {
|
||||
let api = new ApiApi()
|
||||
loading.value = true
|
||||
|
||||
// prevent accidentally clearing the field with extra checkbox
|
||||
if (!updateServings.value) {
|
||||
batchUpdateRequest.value.recipeBatchUpdate.servingsText = undefined
|
||||
}
|
||||
|
||||
api.apiRecipeBatchUpdateUpdate(batchUpdateRequest.value).then(r => {
|
||||
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}).finally(() => {
|
||||
emit('change')
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -3,26 +3,38 @@
|
||||
<v-card :loading="loading">
|
||||
<v-closable-card-title
|
||||
:title="$t('merge_title', {type: $t(genericModel.model.localizationKey)})"
|
||||
:sub-title="genericModel.getLabel(props.source)"
|
||||
:sub-title="sourceNames"
|
||||
:icon="genericModel.model.icon"
|
||||
v-model="dialog"
|
||||
></v-closable-card-title>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-text>
|
||||
{{ $t('merge_selection', {source: genericModel.getLabel(props.source), type: $t(genericModel.model.localizationKey)}) }}
|
||||
{{ $t('merge_selection', {source: sourceNames, type: $t(genericModel.model.localizationKey)}) }}
|
||||
<model-select :model="props.model" v-model="target" allow-create></model-select>
|
||||
|
||||
<v-row>
|
||||
<v-col class="text-center">
|
||||
<v-card color="warning" variant="tonal">
|
||||
<v-card-title>{{ genericModel.getLabel(props.source) }}</v-card-title>
|
||||
</v-card>
|
||||
<v-icon icon="fa-solid fa-arrow-down" class="mt-4 mb-4"></v-icon>
|
||||
<v-card color="success" variant="tonal">
|
||||
<v-card-title v-if="!target">?</v-card-title>
|
||||
<v-card-title v-else>{{ genericModel.getLabel(target) }}</v-card-title>
|
||||
</v-card>
|
||||
<v-col>
|
||||
<v-list>
|
||||
<v-list-item border v-for="item in sourceItems">
|
||||
{{ genericModel.getLabel(item) }}
|
||||
|
||||
<template #append>
|
||||
<v-icon icon="fa-solid fa-xmark" color="error" variant="tonal" v-if="failedItems.includes(item)"></v-icon>
|
||||
<v-icon icon="fa-solid fa-check" color="success" variant="tonal" v-else-if="updatedItems.includes(item)"></v-icon>
|
||||
<v-icon icon="fa-solid fa-circle-notch fa-spin" variant="tonal" color="info" v-else-if="loading"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center">
|
||||
<v-icon icon="fa-solid fa-arrow-down" class="mt-4 mb-4"></v-icon>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center" border>
|
||||
<span v-if="!target">?</span>
|
||||
<span v-else>{{ genericModel.getLabel(target) }}</span>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -30,7 +42,7 @@
|
||||
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn :disabled="loading">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn color="warning" @click="mergeModel()" :loading="loading" :disabled="!target">{{ $t('Merge') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -40,7 +52,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import {PropType, ref} from "vue";
|
||||
import {computed, PropType, ref, watch} from "vue";
|
||||
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models";
|
||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
||||
import {useI18n} from "vue-i18n";
|
||||
@@ -51,7 +63,7 @@ const emit = defineEmits(['change'])
|
||||
|
||||
const props = defineProps({
|
||||
model: {type: String as PropType<EditorSupportedModels>, required: true},
|
||||
source: {type: {} as PropType<EditorSupportedTypes>, required: true},
|
||||
source: {type: Array as PropType<Array<EditorSupportedTypes>>, required: true},
|
||||
activator: {type: String, default: 'parent'},
|
||||
})
|
||||
|
||||
@@ -64,41 +76,62 @@ const automate = ref(false)
|
||||
const genericModel = getGenericModelFromString(props.model, t)
|
||||
const target = ref<null | EditorSupportedTypes>(null)
|
||||
|
||||
const sourceItems = ref<EditorSupportedTypes[]>([])
|
||||
const failedItems = ref<EditorSupportedTypes[]>([])
|
||||
const updatedItems = ref<EditorSupportedTypes[]>([])
|
||||
|
||||
watch(dialog, (newValue, oldValue) => {
|
||||
if (!oldValue && newValue) {
|
||||
sourceItems.value = JSON.parse(JSON.stringify(props.source))
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* generate comma seperated list of item names that act as the source
|
||||
*/
|
||||
const sourceNames = computed(() => {
|
||||
if (sourceItems.value) {
|
||||
return sourceItems.value.map(i => genericModel.getLabel(i)).join(', ')
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
/**
|
||||
* merge source into selected target
|
||||
*/
|
||||
function mergeModel() {
|
||||
let api = new ApiApi()
|
||||
let promises: Promise<any>[] = []
|
||||
|
||||
if (target.value != null) {
|
||||
loading.value = true
|
||||
|
||||
genericModel.merge(props.source, target.value).then(r => {
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||
emit('change', target.value)
|
||||
sourceItems.value.forEach(sourceItem => {
|
||||
promises.push(genericModel.merge(sourceItem, target.value).then(r => {
|
||||
|
||||
if (automate.value && target.value != null && Object.hasOwn(props.source, 'name') && Object.hasOwn(target.value, 'name')) {
|
||||
let automation = {
|
||||
name: `${t('Merge') } ${props.source.name} -> ${target.value.name}`.substring(0,128),
|
||||
param1: props.source.name,
|
||||
param2: target.value.name,
|
||||
type: genericModel.model.mergeAutomation
|
||||
} as Automation
|
||||
api.apiAutomationCreate({automation: automation}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
dialog.value = false
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}).finally(() => {
|
||||
if (!automate.value) {
|
||||
loading.value = false
|
||||
dialog.value = false
|
||||
}
|
||||
updatedItems.value.push(sourceItem)
|
||||
|
||||
if (automate.value && target.value != null && Object.hasOwn(sourceItem, 'name') && Object.hasOwn(sourceItem, 'name')) {
|
||||
let automation = {
|
||||
name: `${t('Merge')} ${sourceItem.name} -> ${target.value.name}`.substring(0, 128),
|
||||
param1: sourceItem.name,
|
||||
param2: target.value.name,
|
||||
type: genericModel.model.mergeAutomation
|
||||
} as Automation
|
||||
promises.push(api.apiAutomationCreate({automation: automation}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}))
|
||||
}
|
||||
}).catch(err => {
|
||||
updatedItems.value.push(sourceItem)
|
||||
}))
|
||||
})
|
||||
|
||||
Promise.allSettled(promises).then(() => {
|
||||
loading.value = false
|
||||
emit('change')
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
<template>
|
||||
<v-card class="mt-1 h-100">
|
||||
<iframe width="100%" height="700px" :src="externalUrl" v-if="isPdf"></iframe>
|
||||
|
||||
<v-img :src="externalUrl" v-if="isImage"></v-img>
|
||||
</v-card>
|
||||
<v-expansion-panels v-model="panelState">
|
||||
<v-expansion-panel value="show">
|
||||
<v-expansion-panel-title>{{ $t('ExternalRecipe') }}</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-card class="mt-1 h-100">
|
||||
<iframe width="100%" height="700px" :src="externalUrl" v-if="isPdf"></iframe>
|
||||
<v-img :src="externalUrl" v-if="isImage"></v-img>
|
||||
</v-card>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, PropType} from "vue";
|
||||
import {computed, onMounted, PropType, ref} from "vue";
|
||||
import {Recipe} from "@/openapi";
|
||||
import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||
import {useUrlSearchParams} from "@vueuse/core";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
recipe: {type: {} as PropType<Recipe>, required: true}
|
||||
})
|
||||
@@ -20,6 +26,15 @@ const props = defineProps({
|
||||
const params = useUrlSearchParams('history')
|
||||
const {getDjangoUrl} = useDjangoUrls()
|
||||
|
||||
const panelState = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
// open panel by default if recipe has not been converted to internal yet or if it does not have any steps
|
||||
if (!props.recipe.internal || props.recipe.steps.length == 0) {
|
||||
panelState.value = 'show'
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* determines if the file is a PDF based on the path
|
||||
*/
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
<template>
|
||||
<!-- <v-row justify="space-between">-->
|
||||
<!-- <v-col>-->
|
||||
<!-- <h2><i class="fas fa-calendar-week fa-fw"></i> Meal Plans</h2>-->
|
||||
<!-- </v-col>-->
|
||||
<!-- </v-row>-->
|
||||
<!-- <v-row justify="space-between">-->
|
||||
<!-- <v-col>-->
|
||||
<!-- <h2><i class="fas fa-calendar-week fa-fw"></i> Meal Plans</h2>-->
|
||||
<!-- </v-col>-->
|
||||
<!-- </v-row>-->
|
||||
|
||||
<v-row class="mt-0" v-if="mealPlanWindows.length > 0">
|
||||
<v-col>
|
||||
<v-window show-arrows>
|
||||
<v-window-item v-for="w in mealPlanWindows" class="pt-1 pb-1">
|
||||
<v-window v-model="currentWindowIndex">
|
||||
<v-window-item v-for="(w, i) in mealPlanWindows" :value="i" class="pt-1 pb-1">
|
||||
<v-row>
|
||||
<v-col v-for="mealPlanGridItem in w">
|
||||
<v-list density="compact" class="pt-0 pb-0">
|
||||
<v-list-item>
|
||||
|
||||
<div class="d-flex justify-space-between">
|
||||
<div class="align-self-center">
|
||||
<v-list-item class="text-center">
|
||||
<div class="d-flex ">
|
||||
<div class="flex-col align-self-start">
|
||||
<v-btn @click="currentWindowIndex--" v-if="currentWindowIndex != 0" icon="fa-solid fa-chevron-left" size="small"></v-btn>
|
||||
</div>
|
||||
<div class="flex-col flex-grow-1 mt-auto mb-auto">
|
||||
{{ mealPlanGridItem.date_label }}
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<v-btn variant="flat" icon="">
|
||||
<i class="fas fa-plus"></i>
|
||||
<model-edit-dialog model="MealPlan" :item-defaults="{fromDate: mealPlanGridItem.date.toJSDate()}" :close-after-create="false" :close-after-save="false"></model-edit-dialog>
|
||||
</v-btn>
|
||||
<div class="flex-col align-self-end">
|
||||
<v-btn @click="currentWindowIndex++" v-if="currentWindowIndex + 1 < mealPlanWindows.length" icon="fa-solid fa-chevron-right"
|
||||
size="small"></v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</v-list-item>
|
||||
<v-divider v-if="mealPlanGridItem.plan_entries.length > 0"></v-divider>
|
||||
<v-list-item v-for="p in mealPlanGridItem.plan_entries" :key="p.id" @click="clickMealPlan(p)" link>
|
||||
@@ -47,7 +46,7 @@
|
||||
<v-menu activator="parent">
|
||||
<v-list>
|
||||
<v-list-item prepend-icon="$edit" link>
|
||||
{{$t('Edit')}}
|
||||
{{ $t('Edit') }}
|
||||
<model-edit-dialog model="MealPlan" :item="p"></model-edit-dialog>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -55,7 +54,11 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center cursor-pointer" variant="tonal">
|
||||
<model-edit-dialog model="MealPlan" :item-defaults="{fromDate: mealPlanGridItem.date.toJSDate()}" :close-after-create="false"
|
||||
:close-after-save="false"></model-edit-dialog>
|
||||
<v-icon icon="$create" size="small"></v-icon>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -79,7 +82,9 @@ import {useRouter} from "vue-router";
|
||||
|
||||
const router = useRouter()
|
||||
const {name} = useDisplay()
|
||||
|
||||
const loading = ref(false)
|
||||
const currentWindowIndex = ref(0)
|
||||
|
||||
let numberOfCols = computed(() => {
|
||||
return homePageCols(name.value)
|
||||
@@ -100,7 +105,12 @@ const meal_plan_grid = computed(() => {
|
||||
grid.push({
|
||||
date: grid_day_date,
|
||||
create_default_date: grid_day_date.toISODate(), // improve meal plan edit modal to do formatting itself and accept dates
|
||||
date_label: grid_day_date.toLocaleString(DateTime.DATE_MED),
|
||||
date_label: grid_day_date.toLocaleString({
|
||||
weekday: 'short',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
year: '2-digit',
|
||||
}),
|
||||
plan_entries: useMealPlanStore().planList.filter((m: MealPlan) => ((DateTime.fromJSDate(m.fromDate).startOf('day') <= grid_day_date.startOf('day')) && (DateTime.fromJSDate((m.toDate != undefined) ? m.toDate : m.fromDate).startOf('day') >= grid_day_date.startOf('day')))),
|
||||
} as MealPlanGridItem)
|
||||
}
|
||||
@@ -134,9 +144,9 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
function clickMealPlan(plan: MealPlan){
|
||||
if(plan.recipe){
|
||||
router.push( {name: 'RecipeViewPage', params: {id: plan.recipe.id}})
|
||||
function clickMealPlan(plan: MealPlan) {
|
||||
if (plan.recipe) {
|
||||
router.push({name: 'RecipeViewPage', params: {id: plan.recipe.id}})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-textarea :model-value="importLog.msg"></v-textarea>
|
||||
<v-textarea :model-value="importLog.msg" max-rows="25" auto-grow></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<div v-if="props.keywords">
|
||||
<slot name="prepend"></slot>
|
||||
|
||||
<v-chip class="me-1 mb-1" :label="props.label" :color="props.color" :size="props.size" :variant="props.variant" v-for="k in keywords"> {{ k.label }}</v-chip>
|
||||
<v-chip class="me-1 mb-1" :label="props.label" :color="props.color" :size="props.size" :variant="props.variant" v-for="k in keywords"
|
||||
:to="useUserPreferenceStore().isAuthenticated ? {name: 'SearchPage', query: {keywords: k.id}} : undefined"> {{ k.label }}
|
||||
</v-chip>
|
||||
|
||||
<slot name="append"></slot>
|
||||
</div>
|
||||
@@ -13,6 +15,7 @@
|
||||
|
||||
import {Keyword, KeywordLabel} from "@/openapi";
|
||||
import {computed, PropType} from "vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
|
||||
const props = defineProps({
|
||||
keywords: Array as PropType<Array<Keyword> | Array<KeywordLabel> | undefined>,
|
||||
@@ -21,11 +24,11 @@ const props = defineProps({
|
||||
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'tonal'},
|
||||
label: {type: Boolean, default: true},
|
||||
// maximum number of keywords, 0 for all
|
||||
maxKeywords : {type: Number, default: 0},
|
||||
maxKeywords: {type: Number, default: 0},
|
||||
})
|
||||
|
||||
const keywords = computed(() => {
|
||||
if (props.maxKeywords > 0){
|
||||
if (props.maxKeywords > 0) {
|
||||
return props.keywords?.slice(0, props.maxKeywords)
|
||||
} else {
|
||||
return props.keywords
|
||||
|
||||
@@ -131,9 +131,9 @@ function refreshVisiblePeriod(startDateUnknown: boolean) {
|
||||
|
||||
// load backwards to as on initial
|
||||
if (startDateUnknown) {
|
||||
useMealPlanStore().refreshFromAPI(DateTime.fromJSDate(calendarDate.value).minus({days: days}).toJSDate(), DateTime.now().plus({days: days}).toJSDate())
|
||||
useMealPlanStore().refreshFromAPI(DateTime.fromJSDate(calendarDate.value).minus({days: days}).toJSDate(), DateTime.fromJSDate(calendarDate.value).plus({days: days}).toJSDate())
|
||||
} else {
|
||||
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: days}).toJSDate())
|
||||
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.fromJSDate(calendarDate.value).plus({days: days}).toJSDate())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,17 @@
|
||||
|
||||
<template v-if="route.name == 'MealPlanPage'">
|
||||
<v-divider></v-divider>
|
||||
<v-list-item prepend-icon="fa-solid fa-calendar-plus" link>
|
||||
{{$t('Auto_Planner')}}
|
||||
<auto-plan-dialog></auto-plan-dialog>
|
||||
</v-list-item>
|
||||
<v-list-subheader>{{$t('Settings')}}</v-list-subheader>
|
||||
<v-list-item>
|
||||
<meal-plan-device-settings></meal-plan-device-settings>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -27,6 +32,7 @@ import {useRoute} from "vue-router";
|
||||
import {getListModels} from "@/types/Models";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import MealPlanDeviceSettings from "@/components/settings/MealPlanDeviceSettings.vue";
|
||||
import AutoPlanDialog from "@/components/dialogs/AutoPlanDialog.vue";
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
|
||||
27
vue3/src/components/display/PrivateRecipeBadge.vue
Normal file
27
vue3/src/components/display/PrivateRecipeBadge.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
<span v-if="props.showText" class="ms-1 me-1">{{ $t('Private_Recipe') }}</span>
|
||||
<v-chip class="me-1 mb-1" :color="props.color" :size="props.size" :variant="props.variant" v-for="u in users" :key="u.id" prepend-icon="fa-solid fa-share-nodes"> {{ u.displayName }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {User} from "@/openapi";
|
||||
import {PropType} from "vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
|
||||
const props = defineProps({
|
||||
showText: {type: Boolean, default: true},
|
||||
users: {type: [] as PropType<Array<User>>, required: false},
|
||||
|
||||
size: {type: String, default: 'x-small'},
|
||||
color: {type: String, default: ''},
|
||||
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'tonal'},
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-card class="mt-2">
|
||||
<v-card class="mt-2" v-if="hasFoodProperties || hasRecipeProperties">
|
||||
<v-card-title>
|
||||
<v-icon icon="$properties"></v-icon>
|
||||
{{ $t('Properties') }}
|
||||
@@ -22,8 +22,8 @@
|
||||
<tbody>
|
||||
<tr v-for="p in propertyList" :key="p.id">
|
||||
<td>{{ p.name }}</td>
|
||||
<td>{{ $n(p.propertyAmountPerServing) }} {{ p.unit }}</td>
|
||||
<td>{{ $n(p.propertyAmountTotal) }} {{ p.unit }}</td>
|
||||
<td>{{ $n(roundDecimals(p.propertyAmountPerServing)) }} {{ p.unit }}</td>
|
||||
<td>{{ $n(roundDecimals(p.propertyAmountTotal)) }} {{ p.unit }}</td>
|
||||
<td v-if="sourceSelectedToShow == 'food'">
|
||||
<v-btn @click="dialogProperty = p; dialog = true" variant="plain" color="warning" icon="fa-solid fa-triangle-exclamation" size="small" class="d-print-none"
|
||||
v-if="p.missingValue"></v-btn>
|
||||
@@ -86,6 +86,7 @@ import {ApiApi, PropertyType, Recipe} from "@/openapi";
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {roundDecimals} from "@/utils/number_utils.ts";
|
||||
|
||||
type PropertyWrapper = {
|
||||
id: number,
|
||||
|
||||
@@ -1,40 +1,11 @@
|
||||
<template>
|
||||
|
||||
<v-card class="mt-1" v-if="cookLogs.length > 0">
|
||||
<v-card-title>{{ $t('Activity') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list>
|
||||
<v-list-item v-for="c in cookLogs.sort((a,b) => a.createdAt! > b.createdAt! ? 1 : -1)" :key="c.id">
|
||||
<template #prepend>
|
||||
<v-avatar color="primary">V</v-avatar>
|
||||
</template>
|
||||
<v-list-item-title class="font-weight-bold">{{ c.createdBy.displayName }}
|
||||
<v-rating density="comfortable" size="x-small" color="tandoor" class="float-right" v-model="c.rating"></v-rating>
|
||||
</v-list-item-title>
|
||||
|
||||
{{ c.comment }}
|
||||
|
||||
<p v-if="c.servings != null && c.servings > 0">
|
||||
{{ c.servings }}
|
||||
<span v-if="recipe.servingsText != ''">{{ recipe.servingsText }}</span>
|
||||
<span v-else-if="c.servings == 1">{{ $t('Serving') }}</span>
|
||||
<span v-else>{{ $t('Servings') }}</span>
|
||||
</p>
|
||||
|
||||
<p class="text-disabled">
|
||||
{{ DateTime.fromJSDate(c.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}
|
||||
</p>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mt-1 d-print-none">
|
||||
<v-card class="mt-1 d-print-none" v-if="useUserPreferenceStore().isAuthenticated" :loading="loading">
|
||||
<v-card-text>
|
||||
<v-textarea :label="$t('Comment')" rows="2" v-model="newCookLog.comment"></v-textarea>
|
||||
<v-row de>
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="4">
|
||||
<v-label>{{$t('Rating')}}</v-label><br/>
|
||||
<v-label>{{ $t('Rating') }}</v-label>
|
||||
<br/>
|
||||
<v-rating v-model="newCookLog.rating" clearable hover density="compact"></v-rating>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
@@ -52,6 +23,48 @@
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mt-1" v-if="cookLogs.length > 0" :loading="loading">
|
||||
<v-card-title>{{ $t('Activity') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list>
|
||||
<v-list-item class="border-t-sm" v-for="c in cookLogs" :key="c.id" :link="c.createdBy.id == useUserPreferenceStore().userSettings?.user.id">
|
||||
<template #prepend>
|
||||
<v-avatar color="primary">{{ c.createdBy.displayName.charAt(0) }}</v-avatar>
|
||||
</template>
|
||||
<v-list-item-title class="font-weight-bold">
|
||||
{{ c.createdBy.displayName }}
|
||||
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ c.comment }}</v-list-item-subtitle>
|
||||
|
||||
<v-list-item-subtitle class="font-italic mt-1" v-if="c.servings != null && c.servings > 0">
|
||||
|
||||
{{ c.servings }}
|
||||
<span v-if="recipe.servingsText != ''">{{ recipe.servingsText }}</span>
|
||||
<span v-else-if="c.servings == 1">{{ $t('Serving') }}</span>
|
||||
<span v-else>{{ $t('Servings') }}</span>
|
||||
|
||||
</v-list-item-subtitle>
|
||||
|
||||
<template #append>
|
||||
<v-list-item-action class="flex-column align-end">
|
||||
<v-rating density="comfortable" size="x-small" color="tandoor" v-model="c.rating" half-increments readonly
|
||||
v-if="c.rating != undefined"></v-rating>
|
||||
<v-spacer></v-spacer>
|
||||
<v-tooltip location="top" :text="DateTime.fromJSDate(c.createdAt).toLocaleString(DateTime.DATETIME_MED)" v-if="c.createdAt != undefined">
|
||||
<template v-slot:activator="{ props }">
|
||||
<span v-bind="props">{{ DateTime.fromJSDate(c.createdAt).toRelative({style: 'narrow'}) }}</span>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
<model-edit-dialog model="CookLog" :item="c" v-if="c.createdBy.id == useUserPreferenceStore().userSettings?.user.id" @save="recLoadCookLog(props.recipe.id)" @delete="recLoadCookLog(props.recipe.id)"></model-edit-dialog>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
@@ -62,6 +75,8 @@ import {ApiApi, CookLog, Recipe} from "@/openapi";
|
||||
import {DateTime} from "luxon";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {VDateInput} from 'vuetify/labs/VDateInput'
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||
|
||||
const props = defineProps({
|
||||
recipe: {
|
||||
@@ -73,21 +88,31 @@ const props = defineProps({
|
||||
const newCookLog = ref({} as CookLog);
|
||||
|
||||
const cookLogs = ref([] as CookLog[])
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
refreshActivity()
|
||||
recLoadCookLog(props.recipe.id)
|
||||
resetForm()
|
||||
})
|
||||
|
||||
/**
|
||||
* load cook logs from database for given recipe
|
||||
* recursively load cook logs from database for given recipe
|
||||
*/
|
||||
function refreshActivity() {
|
||||
function recLoadCookLog(recipeId: number, page: number = 1) {
|
||||
const api = new ApiApi()
|
||||
api.apiCookLogList({recipe: props.recipe.id}).then(r => {
|
||||
// TODO pagination
|
||||
loading.value = true
|
||||
if(page == 1){
|
||||
cookLogs.value = []
|
||||
}
|
||||
api.apiCookLogList({recipe: props.recipe.id, page: page}).then(r => {
|
||||
if (r.results) {
|
||||
cookLogs.value = r.results
|
||||
cookLogs.value = cookLogs.value.concat(r.results)
|
||||
if (r.next) {
|
||||
recLoadCookLog(recipeId, page + 1)
|
||||
} else {
|
||||
cookLogs.value = cookLogs.value.sort((a, b) => a.createdAt! > b.createdAt! ? 1 : -1)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
<template>
|
||||
<template v-if="!props.loading">
|
||||
|
||||
<router-link :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}">
|
||||
<router-link :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :target="linkTarget">
|
||||
<recipe-image :style="{height: props.height}" :recipe="props.recipe" rounded="lg" class="mr-3 ml-3">
|
||||
|
||||
</recipe-image>
|
||||
</router-link>
|
||||
<div class="ml-3">
|
||||
<div class="d-flex ">
|
||||
<div class="flex-grow-1 cursor-pointer" @click="router.push({name: 'RecipeViewPage', params: {id: props.recipe.id}})">
|
||||
<div class="flex-grow-1 cursor-pointer" @click="openRecipe()">
|
||||
<p class="font-weight-bold mt-2">{{ props.recipe.name }}</p>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<!-- <v-btn icon="fas fa-ellipsis-v" size="small" variant="plain"></v-btn>-->
|
||||
<recipe-context-menu :recipe="props.recipe" size="small"></recipe-context-menu>
|
||||
<recipe-context-menu :recipe="props.recipe" size="small" v-if="props.showMenu"></recipe-context-menu>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p class="text-disabled">{{ props.recipe.createdBy.displayName}}</p>-->
|
||||
<keywords-component variant="outlined" :keywords="props.recipe.keywords" :max-keywords="3">
|
||||
<keywords-component variant="outlined" :keywords="props.recipe.keywords" :max-keywords="3" v-if="props.showKeywords">
|
||||
<template #prepend>
|
||||
|
||||
<v-chip class="mb-1 me-1" size="x-small" label variant="outlined" v-if="recipe._private">
|
||||
<private-recipe-badge :show-text="false"></private-recipe-badge>
|
||||
</v-chip>
|
||||
<v-chip class="mb-1 me-1" size="x-small" label variant="outlined" color="info"
|
||||
v-if="!props.recipe.internal">
|
||||
v-if="props.recipe.internal == false">
|
||||
{{ $t('External') }}
|
||||
</v-chip>
|
||||
<v-chip class="mb-1 me-1" size="x-small" prepend-icon="far fa-clock" label variant="outlined"
|
||||
@@ -32,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<v-card :to="`/recipe/${props.recipe.id}`" :style="{'height': props.height}" v-if="false">
|
||||
<v-card :to="{name: 'RecipeViewPage', params: {id: props.recipe.id}}" :style="{'height': props.height}" v-if="false">
|
||||
<v-tooltip
|
||||
class="align-center justify-center"
|
||||
location="top center" origin="overlap"
|
||||
@@ -100,17 +104,32 @@ import {Recipe, RecipeOverview} from "@/openapi";
|
||||
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
|
||||
import RecipeImage from "@/components/display/RecipeImage.vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import PrivateRecipeBadge from "@/components/display/PrivateRecipeBadge.vue";
|
||||
|
||||
const props = defineProps({
|
||||
recipe: {type: {} as PropType<Recipe | RecipeOverview>, required: true,},
|
||||
loading: {type: Boolean, required: false},
|
||||
show_keywords: {type: Boolean, required: false},
|
||||
showKeywords: {type: Boolean, default: true, required: false},
|
||||
show_description: {type: Boolean, required: false},
|
||||
height: {type: String, required: false, default: '15vh'},
|
||||
linkTarget: {type: String, required: false, default: ''},
|
||||
showMenu: {type: Boolean, default: true, required: false}
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
/**
|
||||
* open the recipe either in the same tab or in a new tab depending on the link target prop
|
||||
*/
|
||||
function openRecipe() {
|
||||
if (props.linkTarget != '') {
|
||||
const routeData = router.resolve({name: 'RecipeViewPage', params: {id: props.recipe.id}});
|
||||
window.open(routeData.href, props.linkTarget);
|
||||
} else {
|
||||
router.push({name: 'RecipeViewPage', params: {id: props.recipe.id}})
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
|
||||
<template class="d-block d-lg-none">
|
||||
|
||||
<!-- mobile layout -->
|
||||
<v-card class="rounded-0">
|
||||
<recipe-image
|
||||
max-height="25vh"
|
||||
:recipe="recipe"
|
||||
v-if="recipe.internal"
|
||||
>
|
||||
v-if="recipe.image != undefined">
|
||||
</recipe-image>
|
||||
|
||||
<v-card>
|
||||
@@ -27,47 +27,47 @@
|
||||
</span>
|
||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"></recipe-context-menu>
|
||||
</v-sheet>
|
||||
<keywords-component variant="flat" class="ms-1 mb-2" :keywords="recipe.keywords"></keywords-component>
|
||||
<keywords-component variant="flat" class="ms-1" :keywords="recipe.keywords"></keywords-component>
|
||||
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
|
||||
<v-rating v-model="recipe.rating" size="x-small" v-if="recipe.rating" half-increments readonly></v-rating>
|
||||
<v-sheet class="ps-2 text-disabled">
|
||||
{{ recipe.description }}
|
||||
</v-sheet>
|
||||
</v-card>
|
||||
</v-card>
|
||||
|
||||
<template v-if="recipe.internal">
|
||||
<v-card class="mt-1">
|
||||
<v-container>
|
||||
<v-row class="text-center text-body-2">
|
||||
<v-col class="pt-1 pb-1">
|
||||
<i class="fas fa-cogs fa-fw mr-1"></i> {{ recipe.workingTime }} min<br/>
|
||||
<div class="text-grey">{{ $t('WorkingTime') }}</div>
|
||||
</v-col>
|
||||
<v-col class="pt-1 pb-1">
|
||||
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ recipe.waitingTime }} min</div>
|
||||
<div class="text-grey">{{ $t('WaitingTime') }}</div>
|
||||
</v-col>
|
||||
<v-col class="pt-1 pb-1">
|
||||
<v-card class="mt-1">
|
||||
<v-container>
|
||||
<v-row class="text-center text-body-2">
|
||||
<v-col class="pt-1 pb-1">
|
||||
<i class="fas fa-cogs fa-fw mr-1"></i> {{ recipe.workingTime }} min<br/>
|
||||
<div class="text-grey">{{ $t('WorkingTime') }}</div>
|
||||
</v-col>
|
||||
<v-col class="pt-1 pb-1">
|
||||
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ recipe.waitingTime }} min</div>
|
||||
<div class="text-grey">{{ $t('WaitingTime') }}</div>
|
||||
</v-col>
|
||||
<v-col class="pt-1 pb-1">
|
||||
|
||||
<div class="cursor-pointer">
|
||||
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
|
||||
<div class="text-grey"><span v-if="recipe.servingsText">{{ recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div>
|
||||
<number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings">
|
||||
</number-scaler-dialog>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
<div class="cursor-pointer">
|
||||
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
|
||||
<div class="text-grey"><span v-if="recipe.servingsText">{{ recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div>
|
||||
<number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings">
|
||||
</number-scaler-dialog>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
<!-- Desktop horizontal layout -->
|
||||
<template class="d-none d-lg-block">
|
||||
<v-row dense>
|
||||
<v-col cols="8">
|
||||
<recipe-image
|
||||
:rounded="true"
|
||||
max-height="40vh"
|
||||
:recipe="recipe"
|
||||
v-if="recipe.internal">
|
||||
:recipe="recipe">
|
||||
</recipe-image>
|
||||
</v-col>
|
||||
<v-col cols="4">
|
||||
@@ -75,7 +75,8 @@
|
||||
<v-card-text class="flex-grow-1">
|
||||
<div class="d-flex">
|
||||
<h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1>
|
||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated" class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
|
||||
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"
|
||||
class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
|
||||
</div>
|
||||
<p>
|
||||
{{ $t('created_by') }} {{ recipe.createdBy.displayName }} ({{ DateTime.fromJSDate(recipe.createdAt).toLocaleString(DateTime.DATE_SHORT) }})
|
||||
@@ -84,6 +85,10 @@
|
||||
<i>{{ recipe.description }}</i>
|
||||
</p>
|
||||
|
||||
<private-recipe-badge :users="recipe.shared" v-if="recipe._private"></private-recipe-badge>
|
||||
|
||||
<v-rating v-model="recipe.rating" size="x-small" v-if="recipe.rating" readonly></v-rating>
|
||||
|
||||
<keywords-component variant="flat" class="mt-4" :keywords="recipe.keywords"></keywords-component>
|
||||
|
||||
</v-card-text>
|
||||
@@ -113,20 +118,27 @@
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<template v-if="!recipe.internal">
|
||||
<external-recipe-viewer :recipe="recipe"></external-recipe-viewer>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-card class="mt-1" v-if="recipe.steps.length > 1 && recipe.showIngredientOverview">
|
||||
<steps-overview :steps="recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
|
||||
</v-card>
|
||||
<template v-if="recipe.filePath">
|
||||
<external-recipe-viewer class="mt-2" :recipe="recipe"></external-recipe-viewer>
|
||||
|
||||
<v-card class="mt-1" v-for="(step, index) in recipe.steps" :key="step.id">
|
||||
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
|
||||
<v-card :title="$t('AI')" prepend-icon="$ai" @click="aiConvertRecipe()" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading"
|
||||
v-if="!recipe.internal">
|
||||
<v-card-text>
|
||||
Convert the recipe using AI
|
||||
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<property-view v-model="recipe" :servings="servings" v-if="recipe.internal"></property-view>
|
||||
<v-card class="mt-1" v-if="(recipe.steps.length > 1 || (recipe.steps.length == 1 && !recipe.steps[0].showIngredientsTable)) && recipe.showIngredientOverview">
|
||||
<steps-overview :steps="recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
|
||||
</v-card>
|
||||
|
||||
<v-card class="mt-1" v-for="(step, index) in recipe.steps" :key="step.id">
|
||||
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
|
||||
</v-card>
|
||||
|
||||
<property-view v-model="recipe" :servings="servings"></property-view>
|
||||
|
||||
<v-card class="mt-2">
|
||||
<v-card-text>
|
||||
@@ -136,7 +148,8 @@
|
||||
variant="outlined"
|
||||
:title="$t('CreatedBy')"
|
||||
:subtitle="recipe.createdBy.displayName"
|
||||
prepend-icon="fa-solid fa-user">
|
||||
prepend-icon="fa-solid fa-user"
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdby: recipe.createdBy.id!}}: undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
@@ -144,7 +157,8 @@
|
||||
variant="outlined"
|
||||
:title="$t('Created')"
|
||||
:subtitle="DateTime.fromJSDate(recipe.createdAt).toLocaleString(DateTime.DATETIME_MED)"
|
||||
prepend-icon="$create">
|
||||
prepend-icon="$create"
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {createdon: DateTime.fromJSDate(recipe.createdAt).toISODate()}} : undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3">
|
||||
@@ -152,7 +166,8 @@
|
||||
variant="outlined"
|
||||
:title="$t('Updated')"
|
||||
:subtitle="DateTime.fromJSDate(recipe.updatedAt).toLocaleString(DateTime.DATETIME_MED)"
|
||||
prepend-icon="$edit">
|
||||
prepend-icon="$edit"
|
||||
:to="(useUserPreferenceStore().isAuthenticated) ? {name: 'SearchPage', query: {updatedon: DateTime.fromJSDate(recipe.updatedAt).toISODate()}}: undefined">
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="3" v-if="recipe.sourceUrl">
|
||||
@@ -176,7 +191,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'
|
||||
import {Recipe} from "@/openapi"
|
||||
import {ApiApi, Recipe} from "@/openapi"
|
||||
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue"
|
||||
import StepsOverview from "@/components/display/StepsOverview.vue";
|
||||
import RecipeActivity from "@/components/display/RecipeActivity.vue";
|
||||
@@ -184,23 +199,34 @@ import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
|
||||
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
|
||||
import RecipeImage from "@/components/display/RecipeImage.vue";
|
||||
import ExternalRecipeViewer from "@/components/display/ExternalRecipeViewer.vue";
|
||||
import {useMediaQuery, useWakeLock} from "@vueuse/core";
|
||||
import {useWakeLock} from "@vueuse/core";
|
||||
import StepView from "@/components/display/StepView.vue";
|
||||
import {DateTime} from "luxon";
|
||||
import PropertyView from "@/components/display/PropertyView.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
import {useFileApi} from "@/composables/useFileApi.ts";
|
||||
import PrivateRecipeBadge from "@/components/display/PrivateRecipeBadge.vue";
|
||||
|
||||
const {request, release} = useWakeLock()
|
||||
const {doAiImport, fileApiLoading} = useFileApi()
|
||||
|
||||
const loading = ref(false)
|
||||
const recipe = defineModel<Recipe>({required: true})
|
||||
|
||||
const servings = ref(1)
|
||||
const showFullRecipeName = ref(false)
|
||||
|
||||
/**
|
||||
* factor for multiplying ingredient amounts based on recipe base servings and user selected servings
|
||||
*/
|
||||
const ingredientFactor = computed(() => {
|
||||
return servings.value / ((recipe.value.servings != undefined) ? recipe.value.servings : 1)
|
||||
})
|
||||
|
||||
/**
|
||||
* change servings when recipe servings are changed
|
||||
*/
|
||||
watch(() => recipe.value.servings, () => {
|
||||
if (recipe.value.servings) {
|
||||
servings.value = recipe.value.servings
|
||||
@@ -217,6 +243,43 @@ onBeforeUnmount(() => {
|
||||
release()
|
||||
})
|
||||
|
||||
/**
|
||||
* converts the recipe into an internal recipe using AI
|
||||
*/
|
||||
function aiConvertRecipe() {
|
||||
let api = new ApiApi()
|
||||
|
||||
doAiImport(null, '', recipe.value.id!).then(r => {
|
||||
if (r.recipe) {
|
||||
recipe.value.internal = true
|
||||
recipe.value.steps = r.recipe.steps
|
||||
recipe.value.keywords = r.recipe.keywords
|
||||
recipe.value.servings = r.recipe.servings
|
||||
recipe.value.servingsText = r.recipe.servingsText
|
||||
recipe.value.workingTime = r.recipe.workingTime
|
||||
recipe.value.waitingTime = r.recipe.waitingTime
|
||||
|
||||
servings.value = r.recipe.servings
|
||||
loading.value = true
|
||||
|
||||
api.apiRecipeUpdate({id: recipe.value.id!, recipe: recipe.value}).then(r => {
|
||||
recipe.value = r
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
} else {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, [r.error, r.msg])
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-list-item class="swipe-container" :id="itemContainerId" @touchend="handleSwipe()"
|
||||
<v-list-item class="swipe-container border-t-sm" :id="itemContainerId" @touchend="handleSwipe()"
|
||||
v-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)"
|
||||
>
|
||||
<!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">-->
|
||||
@@ -15,17 +15,16 @@
|
||||
<i class="fas fa-check text-success fa-fw" v-if="a.checked"></i>
|
||||
<i class="fas fa-clock-rotate-left text-info fa-fw" v-if="a.delayed"></i> <b>
|
||||
<span :class="{'text-disabled': a.checked || a.delayed}" class="text-no-wrap">
|
||||
{{ $n(a.amount) }}
|
||||
<span v-if="a.unit">{{ a.unit.name }}</span>
|
||||
<span v-if="amounts.length > 1 || (amounts.length == 1 && a.amount != 1)">{{ $n(a.amount) }}</span>
|
||||
<span class="ms-1" v-if="a.unit">{{ pluralString(a.unit, a.amount) }}</span>
|
||||
</span>
|
||||
|
||||
</b>
|
||||
</span>
|
||||
<br/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-grow-1 align-self-center">
|
||||
{{ shoppingListFood.food.name }} <br/>
|
||||
{{ pluralString(shoppingListFood.food, (amounts.length > 1 || (amounts.length == 1 && amounts[0].amount > 1) ? 2 : 1)) }} <br/>
|
||||
<span v-if="infoRow"><small class="text-disabled">{{ infoRow }}</small></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,6 +58,7 @@ import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
|
||||
import {isDelayed, isEntryVisible, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
|
||||
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
|
||||
import {pluralString} from "@/utils/model_utils.ts";
|
||||
|
||||
const emit = defineEmits(['clicked'])
|
||||
|
||||
|
||||
@@ -123,6 +123,8 @@
|
||||
</template>
|
||||
</v-list>
|
||||
|
||||
<!-- TODO remove once append to body for model select is working properly -->
|
||||
<v-spacer style="margin-top: 120px;"></v-spacer>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -180,7 +182,8 @@
|
||||
<template #append>
|
||||
<v-btn icon="$create" color="create" :disabled="manualAddRecipe == undefined">
|
||||
<v-icon icon="$create"></v-icon>
|
||||
<add-to-shopping-dialog :recipe="manualAddRecipe" v-if="manualAddRecipe != undefined" @created="useShoppingStore().refreshFromAPI(); manualAddRecipe = undefined"></add-to-shopping-dialog>
|
||||
<add-to-shopping-dialog :recipe="manualAddRecipe" v-if="manualAddRecipe != undefined"
|
||||
@created="useShoppingStore().refreshFromAPI(); manualAddRecipe = undefined"></add-to-shopping-dialog>
|
||||
</v-btn>
|
||||
</template>
|
||||
</ModelSelect>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<timer :seconds="step.time != undefined ? step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
|
||||
<v-card-text v-if="step.ingredients.length > 0 || step.instruction != ''">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" v-if="step.ingredients.length > 0">
|
||||
<v-col cols="12" md="6" v-if="step.ingredients.length > 0 && step.showIngredientsTable">
|
||||
<ingredients-table v-model="step.ingredients" :ingredient-factor="ingredientFactor"></ingredients-table>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" class="markdown-body">
|
||||
@@ -35,11 +35,11 @@
|
||||
</v-card-text>
|
||||
|
||||
<template v-if="step.stepRecipe">
|
||||
<v-card-text>
|
||||
<v-card class="mt-1" v-for="(subRecipeStep, subRecipeStepIndex) in step.stepRecipeData.steps" :key="subRecipeStep.id">
|
||||
<v-card class="ma-2 border-md" prepend-icon="$recipes" :title="step.stepRecipeData.name">
|
||||
<v-card-text class="mt-1" v-for="(subRecipeStep, subRecipeStepIndex) in step.stepRecipeData.steps" :key="subRecipeStep.id">
|
||||
<step-view v-model="step.stepRecipeData.steps[subRecipeStepIndex]" :step-number="subRecipeStepIndex+1" :ingredientFactor="ingredientFactor"></step-view>
|
||||
</v-card>
|
||||
</v-card-text>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
<template v-if="step.file">
|
||||
<v-img :src="step.file.preview" v-if="step.file.preview"></v-img>
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
<template>
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title><i class="far fa-list-alt fa-fw me-2"></i> {{$t('StepsOverview')}}</v-expansion-panel-title>
|
||||
<v-expansion-panel-title>
|
||||
<i class="far fa-list-alt fa-fw me-2"></i> {{ $t('StepsOverview') }}
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-container>
|
||||
<v-row v-for="(s, i) in props.steps">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn-toggle density="compact" v-model="useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview" border divided>
|
||||
<v-btn :value="false" prepend-icon="fa-solid fa-folder-tree">{{ $t('Structured') }}</v-btn>
|
||||
<v-btn :value="true" prepend-icon="fa-solid fa-arrows-to-circle">{{ $t('Summary') }}</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-for="(s, i) in props.steps" v-if="!useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview">
|
||||
<v-col class="pa-1" cols="12" md="6">
|
||||
<b v-if="s.showAsHeader">{{ i + 1 }}. {{ s.name }} </b>
|
||||
<ingredients-table v-model="s.ingredients" :ingredient-factor="props.ingredientFactor"></ingredients-table>
|
||||
|
||||
<template v-if="s.stepRecipe">
|
||||
<v-card class="ma-2 border-md" prepend-icon="$recipes" :title="s.stepRecipeData.name"
|
||||
:to="{name: 'RecipeViewPage', params: {id: s.stepRecipeData.id}}" target="_blank">
|
||||
<v-row v-for="subRecipeStep in s.stepRecipeData.steps">
|
||||
<v-col>
|
||||
<ingredients-table v-model="subRecipeStep.ingredients" :ingredient-factor="props.ingredientFactor"></ingredients-table>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-row v-if="useUserPreferenceStore().deviceSettings.recipe_mergeStepOverview">
|
||||
<v-col class="pa-1" cols="12" md="6">
|
||||
<ingredients-table v-model="mergedIngredients" :ingredient-factor="props.ingredientFactor" :show-checkbox="false"></ingredients-table>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
|
||||
@@ -19,9 +45,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {PropType} from 'vue'
|
||||
import {Step} from "@/openapi";
|
||||
import {computed, PropType, ref} from 'vue'
|
||||
import {Ingredient, Step} from "@/openapi";
|
||||
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
|
||||
|
||||
const props = defineProps({
|
||||
steps: {
|
||||
@@ -34,6 +61,70 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const showMergedIngredients = ref(false)
|
||||
|
||||
const mergedIngredients = computed(() => {
|
||||
// Function to collect all ingredients from recipe steps
|
||||
const getAllIngredients = () => {
|
||||
const ingredients: Array<Ingredient> = [];
|
||||
|
||||
// Add ingredients from steps
|
||||
props.steps.forEach(step => {
|
||||
step.ingredients.forEach(ingredient => {
|
||||
if (ingredient.food && !ingredient.isHeader && !ingredient.noAmount) {
|
||||
ingredients.push(ingredient);
|
||||
}
|
||||
});
|
||||
|
||||
// Add ingredients from step recipes if they exist
|
||||
if (step.stepRecipeData) {
|
||||
step.stepRecipeData.steps?.forEach((subStep: Step) => {
|
||||
subStep.ingredients.forEach((ingredient: Ingredient) => {
|
||||
if (ingredient.food && !ingredient.isHeader && !ingredient.noAmount) {
|
||||
ingredients.push(ingredient);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ingredients;
|
||||
};
|
||||
|
||||
// Get all ingredients
|
||||
const allIngredients = getAllIngredients();
|
||||
|
||||
// Create a map to group and sum ingredients by food and unit
|
||||
const groupedIngredients = new Map<string, Ingredient>();
|
||||
|
||||
allIngredients.forEach(ingredient => {
|
||||
if (!ingredient.food || !ingredient.unit) return;
|
||||
|
||||
// Create a unique key for food-unit combination
|
||||
const key = `${ingredient.food.id}-${ingredient.unit.id}`;
|
||||
|
||||
if (groupedIngredients.has(key)) {
|
||||
// If this food-unit combination already exists, sum the amounts
|
||||
const existingIngredient = groupedIngredients.get(key)!;
|
||||
existingIngredient.amount += ingredient.amount;
|
||||
} else {
|
||||
// Create a new entry with the adjusted amount
|
||||
const clonedIngredient = {...ingredient};
|
||||
groupedIngredients.set(key, clonedIngredient);
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the map back to an array
|
||||
const result = Array.from(groupedIngredients.values());
|
||||
|
||||
// Sort alphabetically by food name
|
||||
return result.sort((a, b) => {
|
||||
const foodNameA = a.food?.name.toLowerCase() || '';
|
||||
const foodNameB = b.food?.name.toLowerCase() || '';
|
||||
return foodNameA.localeCompare(foodNameB);
|
||||
});
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
178
vue3/src/components/inputs/HierarchyEditor.vue
Normal file
178
vue3/src/components/inputs/HierarchyEditor.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<v-row justify="space-between" dense>
|
||||
<v-col cols="6">
|
||||
<v-card :loading="loading" variant="outlined">
|
||||
<v-card-text>
|
||||
<v-treeview
|
||||
v-model:activated="activeObjs"
|
||||
return-object
|
||||
activatable
|
||||
rounded
|
||||
indent-lines
|
||||
hide-actions
|
||||
density="compact"
|
||||
open-all
|
||||
item-title="name"
|
||||
:items="objTree"
|
||||
:disabled="loading">
|
||||
<template v-slot:append="{ item, depth, isFirst, isLast }">
|
||||
<v-icon icon="fa-solid fa-location-crosshairs" v-if="item.id == editingObj.id!"></v-icon>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-card v-if="activeObjs.length == 1" :title="activeObjs[0].name" :prepend-icon="genericModel.model.icon" variant="outlined">
|
||||
<v-card-text>
|
||||
<v-label>{{$t('AddChild')}}</v-label>
|
||||
<model-select :model="genericModel.model.name" v-model="addChildObj" allow-create>
|
||||
<template #append>
|
||||
<v-btn color="save" icon="$save" :disabled="addChildObj == undefined" @click="moveObject(addChildObj,activeObjs[0].id!); addChildObj = undefined"></v-btn>
|
||||
</template>
|
||||
</model-select>
|
||||
<v-label>{{$t('Parent')}}</v-label>
|
||||
<model-select :model="genericModel.model.name" v-model="setParentObj" allow-create>
|
||||
<template #append>
|
||||
<v-btn color="save" icon="$save" :disabled="setParentObj == undefined" @click="moveObject(activeObjs[0], setParentObj.id!); setParentObj = undefined"></v-btn>
|
||||
</template>
|
||||
</model-select>
|
||||
|
||||
<v-btn @click="moveObject(activeObjs[0],0)" class="mt-2" color="warning" prepend-icon="fa-solid fa-link-slash" block>
|
||||
{{$t('RemoveParent')}}
|
||||
</v-btn>
|
||||
<v-btn block prepend-icon="$edit" color="info" class="mt-4"
|
||||
:to="{name: 'ModelEditPage' , params: {model: genericModel.model.name, id: activeObjs[0].id }}"
|
||||
v-if="activeObjs[0].id != editingObj.id!">
|
||||
{{ $t('Edit') }}
|
||||
</v-btn>
|
||||
<v-btn block prepend-icon="$search" color="success" class="mt-4 mb-10"
|
||||
:to="searchLink" target="_blank"
|
||||
v-if="searchLink">
|
||||
{{ $t('Recipes') }}
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {computed, onMounted, PropType, ref} from "vue";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
import {EditorSupportedModels, EditorSupportedTypes, getGenericModelFromString} from "@/types/Models.ts";
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
type Tree<T> = T & { children: Tree<T>[] };
|
||||
|
||||
const props = defineProps({
|
||||
model: {type: String as PropType<EditorSupportedModels>, required: true},
|
||||
})
|
||||
|
||||
const editingObj = defineModel<EditorSupportedTypes>({required: true})
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
/**
|
||||
* compute tree structure based on object list
|
||||
*/
|
||||
const objTree = computed(() => {
|
||||
return buildTreeDFS(objList.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* link to search for recipes using the selected object
|
||||
*/
|
||||
const searchLink = computed(() => {
|
||||
if (activeObjs.value.length == 1) {
|
||||
if (props.model == 'Keyword') {
|
||||
return {name: 'SearchPage', query: {keywords: activeObjs.value[0].id!}}
|
||||
} else if (props.model == 'Food') {
|
||||
return {name: 'SearchPage', query: {keywords: activeObjs.value[0].id!}}
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
const objList = ref([] as EditorSupportedTypes[])
|
||||
|
||||
const activeObjs = ref([] as EditorSupportedTypes[])
|
||||
const addChildObj = ref<undefined | EditorSupportedTypes>(undefined)
|
||||
const setParentObj = ref<undefined | EditorSupportedTypes>(undefined)
|
||||
|
||||
const genericModel = ref(getGenericModelFromString(props.model, t))
|
||||
|
||||
onMounted(() => {
|
||||
recLoadObjectTree(editingObj.value.id!, 1)
|
||||
})
|
||||
|
||||
/**
|
||||
* recursively load all objects belonging to the selected objects tree
|
||||
* @param objId base object id to look for tree
|
||||
* @param page page to load
|
||||
*/
|
||||
function recLoadObjectTree(objId: number, page: number) {
|
||||
loading.value = true
|
||||
|
||||
genericModel.value.list({rootTree: objId, pageSize: 100}).then(response => {
|
||||
objList.value = objList.value.concat(response.results)
|
||||
if (response.next) {
|
||||
recLoadObjectTree(objId, page + 1)
|
||||
} else {
|
||||
if (activeObjs.value.length == 0) {
|
||||
activeObjs.value = [objTree.value.find(x => x.id! == editingObj.value.id!)]
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* move the given obj to the desired parent and update in local data cache
|
||||
* @param obj object to change parent for
|
||||
* @param parentId parent id to change the object to or 0 to remove parent
|
||||
*/
|
||||
function moveObject(obj: EditorSupportedTypes, parentId: number) {
|
||||
loading.value = true
|
||||
|
||||
genericModel.value.move(obj, parentId).then((r: any) => {
|
||||
objList.value = []
|
||||
recLoadObjectTree(editingObj.value.id!, 1)
|
||||
}).catch((err: any) => {
|
||||
loading.value = false
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively build a tree datastructures from a DFS ordered list of objects
|
||||
*/
|
||||
function buildTreeDFS<T extends { id: number; parent: number | null }>(items: T[]): Tree<T>[] {
|
||||
let index = 0;
|
||||
|
||||
function buildSubtree(parentId: number | null): Tree<T>[] {
|
||||
const children: Tree<T>[] = [];
|
||||
while (index < items.length && items[index].parent === parentId) {
|
||||
const item = items[index++];
|
||||
const node: Tree<T> = {
|
||||
...item,
|
||||
children: buildSubtree(item.id) // recurse immediately, consumes children in DFS order
|
||||
};
|
||||
children.push(node);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
return buildSubtree(null); // start from roots (parent === null)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -6,9 +6,8 @@
|
||||
<v-list-item :to="{ name: 'ModelEditPage', params: {model: 'recipe', id: recipe.id} }" prepend-icon="$edit">
|
||||
{{ $t('Edit') }}
|
||||
</v-list-item>
|
||||
<v-list-item prepend-icon="$mealplan" link>
|
||||
<v-list-item prepend-icon="$mealplan" @click="mealPlanDialog = true">
|
||||
{{ $t('Add_to_Plan') }}
|
||||
<model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe, servings: recipe.servings}"></model-edit-dialog>
|
||||
</v-list-item>
|
||||
<v-list-item prepend-icon="$shopping" link>
|
||||
{{ $t('Add_to_Shopping') }}
|
||||
@@ -30,11 +29,12 @@
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
|
||||
<model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe, servings: recipe.servings}" :close-after-create="false" :close-after-save="false" v-model="mealPlanDialog"></model-edit-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {nextTick, PropType} from 'vue'
|
||||
import {nextTick, PropType, ref} from 'vue'
|
||||
import {Recipe, RecipeFlat, RecipeOverview} from "@/openapi";
|
||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||
import RecipeShareDialog from "@/components/dialogs/RecipeShareDialog.vue";
|
||||
@@ -46,6 +46,8 @@ const props = defineProps({
|
||||
size: {type: String, default: 'medium'},
|
||||
})
|
||||
|
||||
const mealPlanDialog = ref(false)
|
||||
|
||||
function openPrintView() {
|
||||
print()
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
valueProp="id"
|
||||
label="name"
|
||||
:delay="300"
|
||||
append-to-body
|
||||
:searchable="true"
|
||||
:strict="false"
|
||||
:classes="{
|
||||
@@ -68,9 +67,7 @@ function addIngredient(amount: number, unit: Unit|null, food: Food|null) {
|
||||
food: food,
|
||||
} as ShoppingListEntry
|
||||
|
||||
console.log('adding SLR ? ', props.mealPlan)
|
||||
if (props.mealPlan) {
|
||||
console.log('yes')
|
||||
sle.mealplanId = props.mealPlan.id
|
||||
}
|
||||
|
||||
@@ -134,7 +131,6 @@ function search(query: string) {
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style src="@vueform/multiselect/themes/default.css"></style>
|
||||
<!-- style can't be scoped (for whatever reason) -->
|
||||
<style>
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
<v-number-input :label="$t('Time')" v-model="step.time" :min="0" :step="5" control-variant="split"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" v-if="showRecipe || step.stepRecipe != null">
|
||||
<model-select model="Recipe" v-model="step.stepRecipeData" @update:modelValue="step.stepRecipe = (step.stepRecipeData != null) ? step.stepRecipeData.id! : null"></model-select>
|
||||
<model-select model="Recipe" v-model="step.stepRecipeData"
|
||||
@update:modelValue="step.stepRecipe = (step.stepRecipeData != null) ? step.stepRecipeData.id! : null"></model-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" v-if="showFile || step.file != null">
|
||||
<model-select model="UserFile" v-model="step.file"></model-select>
|
||||
@@ -60,6 +61,10 @@
|
||||
<div v-if="!mobile">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" :empty-insert-threshold="25" group="ingredients">
|
||||
<v-row v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" dense>
|
||||
<v-col cols="12" class="pa-0 ma-0 text-center text-disabled" v-if="ingredient.originalText">
|
||||
<v-icon icon="$import" size="x-small"></v-icon>
|
||||
{{ ingredient.originalText }}
|
||||
</v-col>
|
||||
<v-col cols="2" v-if="!ingredient.isHeader">
|
||||
<v-input hide-details>
|
||||
<template #prepend>
|
||||
@@ -99,26 +104,22 @@
|
||||
<v-list-item link>
|
||||
<v-switch v-model="step.ingredients[index].noAmount" :label="$t('Disable_Amount')" hide-details></v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item @click="editingIngredientIndex = index; dialogIngredientSorter = true" prepend-icon="fa-solid fa-sort">{{
|
||||
$t('Move')
|
||||
}}
|
||||
</v-list-item>
|
||||
<v-list-item v-if="ingredient.originalText" prepend-icon="$import">
|
||||
<v-list-item-title>{{ $t('Original_Text') }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ ingredient.originalText }}</v-list-item-subtitle>
|
||||
<v-list-item @click="editingIngredientIndex = index; dialogIngredientSorter = true" prepend-icon="fa-solid fa-sort">
|
||||
{{ $t('Move') }}
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</vue-draggable>
|
||||
</div>
|
||||
|
||||
<v-list v-if="mobile">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" group="ingredients" empty-insert-threshold="25">
|
||||
<v-list-item v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||
<v-list-item v-for="(ingredient, index) in step.ingredients" :key="ingredient.id" border
|
||||
@click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||
<ingredient-string :ingredient="ingredient"></ingredient-string>
|
||||
<template #append>
|
||||
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
|
||||
@@ -305,6 +306,7 @@ function parseAndInsertIngredients() {
|
||||
r.forEach(i => {
|
||||
console.log(i)
|
||||
step.value.ingredients.push({
|
||||
originalText: i.value.originalText,
|
||||
amount: i.value.amount,
|
||||
food: i.value.food,
|
||||
unit: i.value.unit,
|
||||
|
||||
@@ -12,11 +12,22 @@
|
||||
<v-card-text>
|
||||
<v-form :disabled="loading">
|
||||
|
||||
<v-textarea :label="$t('Comment')" rows="2" v-model="editingObj.comment"></v-textarea>
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="4">
|
||||
<v-label>{{ $t('Rating') }}</v-label>
|
||||
<br/>
|
||||
<v-rating v-model="editingObj.rating" clearable hover density="compact"></v-rating>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-text-field :label="$t('Code')" v-model="editingObj.code"></v-text-field>
|
||||
<v-number-input :label="$t('Servings')" v-model="editingObj.servings" :precision="2"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4">
|
||||
<v-date-input :label="$t('Date')" v-model="editingObj.createdAt"></v-date-input>
|
||||
|
||||
<v-textarea :label="$t('Comment')" v-model="editingObj.comment"></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
@@ -27,19 +38,20 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, watch} from "vue";
|
||||
import {OpenDataFood, OpenDataVersion} from "@/openapi";
|
||||
import {CookLog} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
item: {type: {} as PropType<OpenDataVersion>, required: false, default: null},
|
||||
item: {type: {} as PropType<CookLog>, required: false, default: null},
|
||||
itemId: {type: [Number, String], required: false, default: undefined},
|
||||
itemDefaults: {type: {} as PropType<OpenDataVersion>, required: false, default: {} as OpenDataVersion},
|
||||
itemDefaults: {type: {} as PropType<CookLog>, required: false, default: {} as CookLog},
|
||||
dialog: {type: Boolean, default: false}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<OpenDataVersion>('OpenDataVersion', emit)
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<CookLog>('CookLog', emit)
|
||||
|
||||
/**
|
||||
* watch prop changes and re-initialize editor
|
||||
@@ -58,7 +70,7 @@ onMounted(() => {
|
||||
/**
|
||||
* component specific state setup logic
|
||||
*/
|
||||
function initializeEditor(){
|
||||
function initializeEditor() {
|
||||
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<v-tab value="food">{{ $t('Food') }}</v-tab>
|
||||
<v-tab value="properties">{{ $t('Properties') }}</v-tab>
|
||||
<v-tab value="conversions">{{ $t('Conversion') }}</v-tab>
|
||||
<v-tab value="hierarchy">{{ $t('Hierarchy') }}</v-tab>
|
||||
<v-tab value="misc">{{ $t('Miscellaneous') }}</v-tab>
|
||||
</v-tabs>
|
||||
</v-card-text>
|
||||
@@ -51,6 +52,9 @@
|
||||
<model-select :label="$t('Properties_Food_Unit')" v-model="editingObj.propertiesFoodUnit" model="Unit"></model-select>
|
||||
|
||||
<properties-editor v-model="editingObj.properties" :amount-for="propertiesAmountFor"></properties-editor>
|
||||
|
||||
<!-- TODO remove once append to body for model select is working properly -->
|
||||
<v-spacer style="margin-top: 60px;"></v-spacer>
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
|
||||
@@ -80,7 +84,7 @@
|
||||
</v-col>
|
||||
<v-col md="6">
|
||||
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
|
||||
<model-select v-model="uc.baseUnit" model="Unit" hide-details></model-select>
|
||||
<model-select v-model="uc.baseUnit" model="Unit" hide-details></model-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
@@ -94,13 +98,27 @@
|
||||
</v-col>
|
||||
<v-col md="6">
|
||||
<!-- TODO fix card overflow invisible, overflow-visible class is not working -->
|
||||
<model-select v-model="uc.convertedUnit" model="Unit"></model-select>
|
||||
<model-select v-model="uc.convertedUnit" model="Unit"></model-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
</v-card>
|
||||
</v-form>
|
||||
<!-- TODO remove once append to body for model select is working properly -->
|
||||
<v-spacer style="margin-top: 60px;"></v-spacer>
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="hierarchy">
|
||||
<hierarchy-editor v-model="editingObj" :model="modelClass.model.name"></hierarchy-editor>
|
||||
|
||||
<v-checkbox :label="$t('substitute_siblings')" :hint="$t('substitute_siblings_help')" v-model="editingObj.substituteSiblings" persistent-hint></v-checkbox>
|
||||
<v-checkbox :label="$t('substitute_children')" :hint="$t('substitute_children_help')" v-model="editingObj.substituteChildren" persistent-hint></v-checkbox>
|
||||
|
||||
<ModelSelect model="FoodInheritField" v-model="editingObj.inheritFields" :label="$t('InheritFields')" :hint="$t('InheritFields_help')"
|
||||
mode="tags"></ModelSelect>
|
||||
<ModelSelect model="FoodInheritField" v-model="editingObj.childInheritFields" :label="$t('ChildInheritFields')" :hint="$t('ChildInheritFields_help')"
|
||||
mode="tags"></ModelSelect>
|
||||
</v-tabs-window-item>
|
||||
|
||||
<v-tabs-window-item value="misc">
|
||||
@@ -112,14 +130,6 @@
|
||||
<v-divider class="mt-2 mb-2"></v-divider>
|
||||
<ModelSelect model="Food" v-model="editingObj.substitute" :label="$t('Substitutes')" :hint="$t('substitute_help')" mode="tags"></ModelSelect>
|
||||
|
||||
<v-checkbox :label="$t('substitute_siblings')" :hint="$t('substitute_siblings_help')" v-model="editingObj.substituteSiblings" persistent-hint></v-checkbox>
|
||||
<v-checkbox :label="$t('substitute_children')" :hint="$t('substitute_children_help')" v-model="editingObj.substituteChildren" persistent-hint></v-checkbox>
|
||||
|
||||
<ModelSelect model="FoodInheritField" v-model="editingObj.inheritFields" :label="$t('InheritFields')" :hint="$t('InheritFields_help')"
|
||||
mode="tags"></ModelSelect>
|
||||
<ModelSelect model="FoodInheritField" v-model="editingObj.childInheritFields" :label="$t('ChildInheritFields')" :hint="$t('ChildInheritFields_help')"
|
||||
mode="tags"></ModelSelect>
|
||||
|
||||
<!-- TODO re-add reset inheritance button/api call /function (previously annotated field on food -->
|
||||
<v-text-field :label="$t('Open_Data_Slug')" :hint="$t('open_data_help_text')" persistent-hint v-model="editingObj.openDataSlug" disabled></v-text-field>
|
||||
</v-form>
|
||||
@@ -149,6 +159,7 @@ import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import FdcSearchDialog from "@/components/dialogs/FdcSearchDialog.vue";
|
||||
import {openFdcPage} from "@/utils/fdc.ts";
|
||||
import {DateTime} from "luxon";
|
||||
import HierarchyEditor from "@/components/inputs/HierarchyEditor.vue";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@@ -207,7 +218,7 @@ onMounted(() => {
|
||||
/**
|
||||
* component specific state setup logic
|
||||
*/
|
||||
function initializeEditor(){
|
||||
function initializeEditor() {
|
||||
setupState(props.item, props.itemId, {
|
||||
newItemFunction: () => {
|
||||
editingObj.value.propertiesFoodAmount = 100
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<v-date-input :label="$t('Valid Until')" v-model="editingObj.validUntil"></v-date-input>
|
||||
<v-textarea :label="$t('Note')" v-model="editingObj.internalNote"></v-textarea>
|
||||
<v-checkbox :label="$t('Reusable')" v-model="editingObj.reusable"></v-checkbox>
|
||||
<v-text-field :label="$t('Link')" readonly :model-value="inviteLinkUrl(editingObj)">
|
||||
<template #append-inner>
|
||||
<btn-copy variant="plain" color="undefined" :copy-value="inviteLinkUrl(editingObj)"></btn-copy>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</model-editor-base>
|
||||
@@ -31,6 +36,7 @@ import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {DateTime} from "luxon";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
import BtnCopy from "@/components/buttons/BtnCopy.vue";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@@ -79,6 +85,16 @@ function initializeEditor(){
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* returns url for invite link
|
||||
* @param inviteLink InviteLink object to create url for
|
||||
*/
|
||||
function inviteLinkUrl(inviteLink: InviteLink) {
|
||||
return `${location.protocol}//${location.host}/invite/${inviteLink.uuid}`
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -9,13 +9,29 @@
|
||||
:is-changed="editingObjChanged"
|
||||
:model-class="modelClass"
|
||||
:object-name="editingObjName()">
|
||||
|
||||
<v-card-text class="pa-0">
|
||||
<v-tabs v-model="tab" :disabled="loading" grow>
|
||||
<v-tab value="keyword">{{ $t('Keyword') }}</v-tab>
|
||||
<v-tab value="hierarchy">{{ $t('Hierarchy') }}</v-tab>
|
||||
</v-tabs>
|
||||
</v-card-text>
|
||||
|
||||
|
||||
<v-card-text>
|
||||
<v-form :disabled="loading">
|
||||
<v-tabs-window v-model="tab">
|
||||
<v-tabs-window-item value="keyword">
|
||||
<v-form :disabled="loading">
|
||||
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description"></v-textarea>
|
||||
|
||||
</v-form>
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
<v-tabs-window-item value="hierarchy">
|
||||
<hierarchy-editor v-model="editingObj" :model="modelClass.model.name"></hierarchy-editor>
|
||||
</v-tabs-window-item>
|
||||
</v-tabs-window>
|
||||
</v-card-text>
|
||||
</model-editor-base>
|
||||
|
||||
@@ -23,10 +39,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, watch} from "vue";
|
||||
import {onMounted, PropType, ref, watch} from "vue";
|
||||
import {Keyword} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
import HierarchyEditor from "@/components/inputs/HierarchyEditor.vue";
|
||||
|
||||
const props = defineProps({
|
||||
item: {type: {} as PropType<Keyword>, required: false, default: null},
|
||||
@@ -38,6 +55,8 @@ const props = defineProps({
|
||||
const emit = defineEmits(['create', 'save', 'delete', 'close', 'changedState'])
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<Keyword>('Keyword', emit)
|
||||
|
||||
const tab = ref("keyword")
|
||||
|
||||
/**
|
||||
* watch prop changes and re-initialize editor
|
||||
* required to embed editor directly into pages and be able to change item from the outside
|
||||
@@ -55,7 +74,7 @@ onMounted(() => {
|
||||
/**
|
||||
* component specific state setup logic
|
||||
*/
|
||||
function initializeEditor(){
|
||||
function initializeEditor() {
|
||||
setupState(props.item, props.itemId, {itemDefaults: props.itemDefaults})
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@update:modelValue="editingObj.servings = editingObj.recipe ? editingObj.recipe.servings : 1"></ModelSelect>
|
||||
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
|
||||
<!--TODO create days input with +/- synced to date -->
|
||||
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe"></recipe-card>
|
||||
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe" link-target="_blank"></recipe-card>
|
||||
<v-btn prepend-icon="$shopping" color="create" class="mt-1" v-if="!editingObj.shopping && editingObj.recipe && isUpdate()">
|
||||
{{$t('Add')}}
|
||||
<add-to-shopping-dialog :recipe="editingObj.recipe" :meal-plan="editingObj" @created="loadShoppingListEntries(); editingObj.shopping = true;"></add-to-shopping-dialog>
|
||||
@@ -47,6 +47,7 @@
|
||||
multiple="range"
|
||||
prepend-icon=""
|
||||
prepend-inner-icon="$calendar"
|
||||
hide-details
|
||||
></v-date-input>
|
||||
|
||||
<v-input>
|
||||
@@ -77,7 +78,7 @@
|
||||
<v-tabs-window-item value="shopping">
|
||||
<closable-help-alert class="mb-2" :text="$t('MealPlanShoppingHelp')"></closable-help-alert>
|
||||
|
||||
<v-row v-if="isUpdate()" dense style="max-height: 75vh" class="overflow-y-scroll">
|
||||
<v-row v-if="isUpdate()" dense style="max-height: 75vh; min-height: 30vh" class="overflow-y-scroll">
|
||||
<v-col>
|
||||
<shopping-list-entry-input :loading="useShoppingStore().currentlyUpdating" :meal-plan="editingObj"></shopping-list-entry-input>
|
||||
<v-list v-if="editingObj.id">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512" rows="2" auto-grow></v-textarea>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512" maxlength="512" rows="2" auto-grow></v-textarea>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
@@ -50,21 +50,21 @@
|
||||
<model-select mode="tags" v-model="editingObj.keywords" model="Keyword" allow-create></model-select>
|
||||
<v-row dense>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :label="$t('WaitingTime')" v-model="editingObj.waitingTime"></v-text-field>
|
||||
<v-number-input :label="$t('WaitingTime')" v-model="editingObj.waitingTime" :step="5"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :label="$t('WorkingTime')" v-model="editingObj.workingTime"></v-text-field>
|
||||
<v-number-input :label="$t('WorkingTime')" v-model="editingObj.workingTime" :step="5"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :label="$t('Servings')" v-model="editingObj.servings"></v-text-field>
|
||||
<v-number-input :label="$t('Servings')" v-model="editingObj.servings"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field :label="$t('ServingsText')" v-model="editingObj.servingsText"></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- <closable-help-alert :text="$t('RecipeStepsHelp')" :action-text="$t('Steps')" @click="tab='steps'"></closable-help-alert>-->
|
||||
<v-btn @click="tab='steps'" class="float-right" variant="tonal" append-icon="fa-solid fa-arrow-right">{{$t('Steps')}} </v-btn>
|
||||
<!-- <closable-help-alert :text="$t('RecipeStepsHelp')" :action-text="$t('Steps')" @click="tab='steps'"></closable-help-alert>-->
|
||||
<v-btn @click="tab='steps'" class="float-right" variant="tonal" append-icon="fa-solid fa-arrow-right">{{ $t('Steps') }}</v-btn>
|
||||
</v-form>
|
||||
|
||||
</v-tabs-window-item>
|
||||
@@ -77,12 +77,19 @@
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col class="text-center">
|
||||
<v-btn-group density="compact">
|
||||
<v-btn-group density="compact" divided border>
|
||||
<v-btn color="success" prepend-icon="fa-solid fa-plus" @click="addStep()">{{ $t('Add_Step') }}</v-btn>
|
||||
<v-btn color="warning" @click="dialogStepManager = true">
|
||||
<v-btn color="warning" @click="dialogStepManager = true" :disabled="editingObj.steps.length < 2">
|
||||
<v-icon icon="fa-solid fa-arrow-down-1-9"></v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn prepend-icon="fa-solid fa-maximize" @click="handleSplitAllSteps" :disabled="editingObj.steps.length < 1"><span
|
||||
v-if="!mobile">{{ $t('Split') }}</span></v-btn>
|
||||
<v-btn prepend-icon="fa-solid fa-minimize" @click="handleMergeAllSteps" :disabled="editingObj.steps.length < 2"><span
|
||||
v-if="!mobile">{{ $t('Merge') }}</span></v-btn>
|
||||
</v-btn-group>
|
||||
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -100,17 +107,26 @@
|
||||
v-model="editingObj.showIngredientOverview"></v-checkbox>
|
||||
|
||||
<v-text-field :label="$t('Imported_From')" v-model="editingObj.sourceUrl"></v-text-field>
|
||||
<v-checkbox :label="$t('Private_Recipe')" persistent-hint v-model="editingObj._private"></v-checkbox>
|
||||
<model-select mode="tags" model="User" :label="$t('Private_Recipe')" :hint="$t('Private_Recipe_Help')" persistent-hint v-model="editingObj.shared"
|
||||
<v-checkbox :label="$t('Private_Recipe')" persistent-hint :hint="$t('Private_Recipe_Help')" v-model="editingObj._private"></v-checkbox>
|
||||
<model-select mode="tags" model="User" :label="$t('Share')" persistent-hint v-model="editingObj.shared"
|
||||
append-to-body v-if="editingObj._private"></model-select>
|
||||
|
||||
<div class="mt-2" v-if="editingObj.filePath">
|
||||
{{ $t('ExternalRecipe') }}
|
||||
<v-text-field readonly v-model="editingObj.filePath"></v-text-field>
|
||||
|
||||
<v-btn prepend-icon="$delete" color="error" :loading="loading">{{ $t('delete_title', {type: $t('ExternalRecipe')}) }}
|
||||
<delete-confirm-dialog :object-name="editingObj.filePath" :model-name="$t('ExternalRecipe')" @delete="deleteExternalFile()"></delete-confirm-dialog>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
</v-tabs-window>
|
||||
</v-card-text>
|
||||
<v-card-text v-if="isSpaceAtRecipeLimit(useUserPreferenceStore().activeSpace)">
|
||||
<v-alert color="warning" icon="fa-solid fa-triangle-exclamation">
|
||||
{{$t('SpaceLimitReached')}}
|
||||
{{ $t('SpaceLimitReached') }}
|
||||
<v-btn color="success" variant="flat" :to="{name: 'SpaceSettings'}">{{ $t('SpaceSettings') }}</v-btn>
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
@@ -138,7 +154,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref, shallowRef, watch} from "vue";
|
||||
import {Ingredient, Recipe, Step} from "@/openapi";
|
||||
import {ApiApi, Ingredient, Recipe, Step} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
@@ -151,7 +167,9 @@ import ClosableHelpAlert from "@/components/display/ClosableHelpAlert.vue";
|
||||
import {useDisplay} from "vuetify";
|
||||
import {isSpaceAtRecipeLimit} from "@/utils/logic_utils";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import SpaceSettings from "@/components/settings/SpaceSettings.vue";
|
||||
import {mergeAllSteps, splitAllSteps} from "@/utils/step_utils.ts";
|
||||
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@@ -188,7 +206,7 @@ onMounted(() => {
|
||||
/**
|
||||
* component specific state setup logic
|
||||
*/
|
||||
function initializeEditor(){
|
||||
function initializeEditor() {
|
||||
setupState(props.item, props.itemId, {
|
||||
newItemFunction: () => {
|
||||
editingObj.value.steps = [] as Step[]
|
||||
@@ -249,6 +267,33 @@ function deleteStepAtIndex(index: number) {
|
||||
editingObj.value.steps.splice(index, 1)
|
||||
}
|
||||
|
||||
function handleMergeAllSteps(): void {
|
||||
if (editingObj.value.steps) {
|
||||
mergeAllSteps(editingObj.value.steps)
|
||||
}
|
||||
}
|
||||
|
||||
function handleSplitAllSteps(): void {
|
||||
if (editingObj.value.steps) {
|
||||
splitAllSteps(editingObj.value.steps, '\n')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the external file for the recipe
|
||||
*/
|
||||
function deleteExternalFile() {
|
||||
let api = new ApiApi()
|
||||
loading.value = true
|
||||
api.apiRecipeDeleteExternalPartialUpdate({id: editingObj.value.id!, patchedRecipe: editingObj.value}).then(r => {
|
||||
editingObj.value = r
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.DELETE_ERROR, err)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<v-divider class="mb-3"></v-divider>
|
||||
|
||||
<v-row>
|
||||
<database-link-col prepend-icon="fa-solid fa-terminal" :href="useDjangoUrls().getDjangoUrl('api')" :lg="6" :title="$t('API Browser')"></database-link-col>
|
||||
<database-link-col prepend-icon="fa-solid fa-terminal" :href="useDjangoUrls().getDjangoUrl('api')" :lg="6" :title="$t('API_Browser')"></database-link-col>
|
||||
<database-link-col prepend-icon="fa-solid fa-laptop-code" :href="useDjangoUrls().getDjangoUrl('/docs/api/')" :lg="6"
|
||||
:title="$t('API Dokumentation')"></database-link-col>
|
||||
:title="$t('API_Documentation')"></database-link-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<v-divider class="mb-3"></v-divider>
|
||||
|
||||
<v-text-field v-model="useUserPreferenceStore().userSettings.defaultUnit" :label="$t('Default_Unit')"></v-text-field>
|
||||
<!-- <v-text-field v-model="useUserPreferenceStore().userSettings.ingredientDecimals" :label="$t('Decimals')"></v-text-field>-->
|
||||
<v-number-input v-model="useUserPreferenceStore().userSettings.ingredientDecimals" :label="$t('Decimals')"></v-number-input>
|
||||
|
||||
<!-- <v-select-->
|
||||
<!-- :label="$t('DefaultPage')"-->
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
<v-divider class="mb-3"></v-divider>
|
||||
|
||||
<v-data-table :items="spaceInviteLinks" :headers="inviteTableHeaders" density="compact" :hide-default-footer="spaceInviteLinks.length < 10">
|
||||
<template #item.reusable="{item}">
|
||||
<v-icon icon="fa-solid fa-check" color="success" v-if="item.reusable"></v-icon>
|
||||
<v-icon icon="fa-solid fa-times" color="error" v-if="!item.reusable"></v-icon>
|
||||
</template>
|
||||
|
||||
<template #item.edit="{item}">
|
||||
<btn-copy size="small" :copy-value="inviteLinkUrl(item)" class="me-1"></btn-copy>
|
||||
<v-btn color="edit" size="small">
|
||||
@@ -65,6 +70,7 @@ const inviteTableHeaders = [
|
||||
{title: 'ID', key: 'id'},
|
||||
{title: t('Email'), key: 'email'},
|
||||
{title: t('Role'), key: 'group.name'},
|
||||
{title: t('Reusable'), key: 'reusable'},
|
||||
{title: t('Edit'), key: 'edit', align: 'end'},
|
||||
]
|
||||
|
||||
@@ -77,7 +83,7 @@ onMounted(() => {
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
})
|
||||
|
||||
api.apiInviteLinkList().then(r => {
|
||||
api.apiInviteLinkList({unused: true}).then(r => {
|
||||
spaceInviteLinks.value = r.results
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
|
||||
@@ -84,8 +84,9 @@ export function useFileApi() {
|
||||
* uploads the given file to the image recognition endpoint
|
||||
* @param file file object to upload
|
||||
* @param text text to import
|
||||
* @param recipeId id of a recipe to use as import base (for external recipes
|
||||
*/
|
||||
function doAiImport(file: File | null, text: string = '') {
|
||||
function doAiImport(file: File | null, text: string = '', recipeId: string = '') {
|
||||
let formData = new FormData()
|
||||
|
||||
if (file != null) {
|
||||
@@ -94,6 +95,8 @@ export function useFileApi() {
|
||||
formData.append('file', '')
|
||||
}
|
||||
formData.append('text', text)
|
||||
formData.append('recipe_id', recipeId)
|
||||
fileApiLoading.value = true
|
||||
|
||||
return fetch(getDjangoUrl(`api/ai-import/`), {
|
||||
method: 'POST',
|
||||
|
||||
@@ -5,6 +5,7 @@ import {useI18n} from "vue-i18n";
|
||||
import {ResponseError} from "@/openapi";
|
||||
import {getNestedProperty} from "@/utils/utils";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import {useTitle} from "@vueuse/core";
|
||||
|
||||
// TODO type emit parameter (https://mokkapps.de/vue-tips/emit-event-from-composable)
|
||||
// TODO alternatively there seems to be a getContext method to get the calling context (good practice?)
|
||||
@@ -18,6 +19,7 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
|
||||
const editingObjChanged = ref(false)
|
||||
|
||||
const {t} = useI18n()
|
||||
const title = useTitle()
|
||||
|
||||
/**
|
||||
* watch editing object to detect changes
|
||||
@@ -108,12 +110,14 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
|
||||
newItemFunction()
|
||||
|
||||
loading.value = false
|
||||
title.value = editingObjName()
|
||||
return Promise.resolve(editingObj.value)
|
||||
} else if (item !== null) {
|
||||
// item is given so return that
|
||||
editingObj.value = item
|
||||
existingItemFunction()
|
||||
loading.value = false
|
||||
title.value = editingObjName()
|
||||
return Promise.resolve(editingObj.value)
|
||||
} else if (itemId !== undefined && itemId != '') {
|
||||
// itemId is given => fetch from server and return item
|
||||
@@ -126,6 +130,7 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
|
||||
return modelClass.value.retrieve(itemId).then((r: T) => {
|
||||
editingObj.value = r
|
||||
existingItemFunction()
|
||||
title.value = editingObjName()
|
||||
return editingObj.value
|
||||
}).catch((err: any) => {
|
||||
if (err instanceof ResponseError && err.response.status == 404) {
|
||||
|
||||
@@ -25,7 +25,9 @@ export function useNavigation() {
|
||||
TANDOOR_PLUGINS.forEach(plugin => {
|
||||
plugin.navigationDrawer.forEach(navEntry => {
|
||||
let navEntryCopy = Object.assign({}, navEntry)
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
if ('title' in navEntryCopy) {
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
}
|
||||
navigation.push(navEntryCopy)
|
||||
})
|
||||
})
|
||||
@@ -44,7 +46,9 @@ export function useNavigation() {
|
||||
TANDOOR_PLUGINS.forEach(plugin => {
|
||||
plugin.bottomNavigation.forEach(navEntry => {
|
||||
let navEntryCopy = Object.assign({}, navEntry)
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
if ('title' in navEntryCopy) {
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
}
|
||||
navigation.push(navEntryCopy)
|
||||
})
|
||||
})
|
||||
@@ -80,7 +84,9 @@ export function useNavigation() {
|
||||
TANDOOR_PLUGINS.forEach(plugin => {
|
||||
plugin.userNavigation.forEach(navEntry => {
|
||||
let navEntryCopy = Object.assign({}, navEntry)
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
if ('title' in navEntryCopy) {
|
||||
navEntryCopy.title = t(navEntryCopy.title)
|
||||
}
|
||||
navigation.push(navEntryCopy)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Add": "",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "",
|
||||
"AddToShopping": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -17,8 +20,13 @@
|
||||
"Auto_Planner": "",
|
||||
"Automate": "",
|
||||
"Automation": "",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "",
|
||||
"Cancel": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
@@ -52,16 +60,19 @@
|
||||
"Create_New_Unit": "",
|
||||
"Current_Period": "",
|
||||
"Custom Filter": "",
|
||||
"DELETE_ERROR": "",
|
||||
"Date": "",
|
||||
"DelayFor": "",
|
||||
"DelayUntil": "",
|
||||
"Delete": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "",
|
||||
"Delete_Keyword": "",
|
||||
"Description": "",
|
||||
"Disable_Amount": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Edit": "",
|
||||
@@ -78,7 +89,9 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "",
|
||||
"Fats": "",
|
||||
"File": "",
|
||||
@@ -99,6 +112,7 @@
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Hide_as_header": "",
|
||||
"Hierarchy": "",
|
||||
"Icon": "",
|
||||
"IgnoreAccents": "",
|
||||
"IgnoreAccentsHelp": "",
|
||||
@@ -138,6 +152,7 @@
|
||||
"Log_Recipe_Cooking": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "",
|
||||
"Meal_Plan": "",
|
||||
"Meal_Plan_Days": "",
|
||||
@@ -145,6 +160,7 @@
|
||||
"Meal_Type_Required": "",
|
||||
"Meal_Types": "",
|
||||
"Merge": "",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "",
|
||||
"Message": "",
|
||||
"MissingProperties": "",
|
||||
@@ -169,6 +185,7 @@
|
||||
"New_Unit": "",
|
||||
"Next_Day": "",
|
||||
"Next_Period": "",
|
||||
"No": "",
|
||||
"NoCategory": "",
|
||||
"NoUnit": "",
|
||||
"No_ID": "",
|
||||
@@ -202,6 +219,8 @@
|
||||
"Previous_Day": "",
|
||||
"Previous_Period": "",
|
||||
"Print": "",
|
||||
"Private": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Protected": "",
|
||||
"Proteins": "",
|
||||
"Quick actions": "",
|
||||
@@ -216,7 +235,9 @@
|
||||
"Recipes": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Reset": "",
|
||||
"Reset_Search": "",
|
||||
@@ -247,6 +268,7 @@
|
||||
"Single": "",
|
||||
"Size": "",
|
||||
"Sort_by_new": "",
|
||||
"Space": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
@@ -270,6 +292,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "",
|
||||
"Unit": "",
|
||||
"Unit_Alias": "",
|
||||
@@ -285,6 +308,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "",
|
||||
"View_Recipes": "",
|
||||
"Visibility": "",
|
||||
"Waiting": "",
|
||||
"Warning": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -292,6 +316,7 @@
|
||||
"Week": "",
|
||||
"Week_Numbers": "",
|
||||
"Year": "",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Add": "Добави",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Добавете {food} към списъка си за пазаруване",
|
||||
"AddToShopping": "Добавяне към списъка за пазаруване",
|
||||
"Add_Servings_to_Shopping": "Добавете {servings} порции към Пазаруване",
|
||||
@@ -17,8 +20,13 @@
|
||||
"Auto_Planner": "Автоматичен плановик",
|
||||
"Automate": "Автоматизация",
|
||||
"Automation": "Автоматизация",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Книжен пазар",
|
||||
"Books": "Книги",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "Калории",
|
||||
"Cancel": "Откажи",
|
||||
"Cannot_Add_Notes_To_Shopping": "Бележки не могат да се добавят към списъка за пазаруване",
|
||||
@@ -49,16 +57,19 @@
|
||||
"Create_New_Unit": "Добавяне на нова единица",
|
||||
"Current_Period": "Текущ период",
|
||||
"Custom Filter": "Персонализиран филтър",
|
||||
"DELETE_ERROR": "",
|
||||
"Date": "Дата",
|
||||
"DelayFor": "Закъснение за {hours} часа",
|
||||
"DelayUntil": "Забавяне до",
|
||||
"Delete": "Изтрий",
|
||||
"DeleteShoppingConfirm": "Сигурни ли сте, че искате да премахнете цялата {food} от списъка за пазаруване?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "Изтриване на храна",
|
||||
"Delete_Keyword": "Изтриване на ключова дума",
|
||||
"Description": "Описание",
|
||||
"Disable_Amount": "Деактивиране на сумата",
|
||||
"Documentation": "Документация",
|
||||
"DontChange": "",
|
||||
"Download": "Изтегляне",
|
||||
"Drag_Here_To_Delete": "Плъзнете тук, за да изтриете",
|
||||
"Edit": "Редактиране",
|
||||
@@ -75,7 +86,9 @@
|
||||
"Export_Supported": "Поддържа се експорт",
|
||||
"Export_To_ICal": "Експортиране на .ics",
|
||||
"External": "Външен",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Външно изображение на рецептата",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Неуспешно",
|
||||
"Fats": "Мазнини",
|
||||
"File": "Файл",
|
||||
@@ -96,6 +109,7 @@
|
||||
"Hide_Keywords": "Скриване на ключова дума",
|
||||
"Hide_Recipes": "Скриване на рецепти",
|
||||
"Hide_as_header": "Скриване като заглавка",
|
||||
"Hierarchy": "",
|
||||
"Icon": "Икона",
|
||||
"IgnoreAccents": "",
|
||||
"IgnoreAccentsHelp": "",
|
||||
@@ -133,6 +147,7 @@
|
||||
"Log_Recipe_Cooking": "Дневник на Рецепта за готвене",
|
||||
"Make_Header": "Направете заглавие",
|
||||
"Make_Ingredient": "Направете съставка",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Управление на Книги",
|
||||
"Meal_Plan": "План на хранене",
|
||||
"Meal_Plan_Days": "Бъдещи планове за хранене",
|
||||
@@ -140,6 +155,7 @@
|
||||
"Meal_Type_Required": "Изисква се вид хранене",
|
||||
"Meal_Types": "Видове хранене",
|
||||
"Merge": "Обединяване",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Обединяване на ключова дума",
|
||||
"MissingProperties": "",
|
||||
"Month": "Месец",
|
||||
@@ -162,6 +178,7 @@
|
||||
"New_Unit": "Нова единица",
|
||||
"Next_Day": "Следващия ден",
|
||||
"Next_Period": "Следващ период",
|
||||
"No": "",
|
||||
"NoCategory": "Няма избрана категория.",
|
||||
"NoUnit": "",
|
||||
"No_ID": "Идентификатора не е намерен, не може да се изтрие.",
|
||||
@@ -195,6 +212,8 @@
|
||||
"Previous_Day": "Предишен ден",
|
||||
"Previous_Period": "Предишен период",
|
||||
"Print": "Печат",
|
||||
"Private": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Protected": "Защитен",
|
||||
"Proteins": "Протеини (белтъчини)",
|
||||
"Quick actions": "Бързи действия",
|
||||
@@ -209,7 +228,9 @@
|
||||
"Recipes": "Рецепти",
|
||||
"Recipes_In_Import": "Рецепти във вашия файл за импортиране",
|
||||
"Recipes_per_page": "Рецепти на страница",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Премахнете {food} от списъка си за пазаруване",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Изтрийте хранителните стойности от рецептата",
|
||||
"Reset": "Нулиране",
|
||||
"Reset_Search": "Нулиране на търсенето",
|
||||
@@ -240,6 +261,7 @@
|
||||
"Single": "Единичен",
|
||||
"Size": "Размер",
|
||||
"Sort_by_new": "Сортиране по ново",
|
||||
"Space": "",
|
||||
"Starting_Day": "Начален ден от седмицата",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
@@ -263,6 +285,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Тип",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Недефиниран",
|
||||
"Unit": "Единица",
|
||||
"Unit_Alias": "Псевдоним на единица",
|
||||
@@ -276,6 +299,7 @@
|
||||
"User": "потребител",
|
||||
"View": "Изглед",
|
||||
"View_Recipes": "Вижте рецепти",
|
||||
"Visibility": "",
|
||||
"Waiting": "Очакване",
|
||||
"Warning": "Внимание",
|
||||
"Warning_Delete_Supermarket_Category": "Изтриването на категория супермаркет ще изтрие и всички връзки с храни. Сигурен ли си?",
|
||||
@@ -283,6 +307,7 @@
|
||||
"Week": "Седмица",
|
||||
"Week_Numbers": "Номера на седмиците",
|
||||
"Year": "Година",
|
||||
"Yes": "",
|
||||
"add_keyword": "Добавяне на ключова дума",
|
||||
"additional_options": "Допълнителни настройки",
|
||||
"advanced": "Разширено",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Compte",
|
||||
"Add": "Afegir",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Afegeix {food} a la llista de la compra",
|
||||
"AddToShopping": "Afegir a la llista de la compra",
|
||||
"Add_Servings_to_Shopping": "Afegir {servings} racions a la compra",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Automatitzar",
|
||||
"Automation": "Automatizació",
|
||||
"Back": "Enrere",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Marcadors",
|
||||
"Books": "Llibres",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Calculadora",
|
||||
"Calories": "Calories",
|
||||
"Cancel": "Cancelar",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "Pengeu una imatge per utilitzar com a logotip a la barra de navegació.",
|
||||
"CustomTheme": "Tema Personalitzat",
|
||||
"CustomThemeHelp": "Cancel·la els estils del tema seleccionat Carregant un fitxer CSS personalitzat.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Millora el teu Espai important llistes d’aliments, unitats i més, seleccionats per la comunitat per millorar la teva col·lecció de receptes.",
|
||||
"Datatype": "Tipus de Dades",
|
||||
"Date": "Data",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "Endarrerir fins",
|
||||
"Delete": "Eliminar",
|
||||
"DeleteShoppingConfirm": "Segur que vols eliminar tot el/la {food} de la llista de la compra?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Eliminar tot",
|
||||
"Delete_Food": "Eliminar Aliment",
|
||||
"Delete_Keyword": "Esborreu paraula clau",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "Deshabiliteu quantitat",
|
||||
"Disabled": "Desactivat",
|
||||
"Documentation": "Documentació",
|
||||
"DontChange": "",
|
||||
"Download": "Descarregar",
|
||||
"Drag_Here_To_Delete": "Arrossega aquí per a eliminar",
|
||||
"Edit": "Editar",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "Exportació suportada",
|
||||
"Export_To_ICal": "Exportar .ics",
|
||||
"External": "Extern",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Imatge externa de la recepta",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "Base de dades FDC ID",
|
||||
"FDC_Search": "Cerca FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Error",
|
||||
"Fats": "Greixos",
|
||||
"File": "Arxiu",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "Amagueu paraula clau",
|
||||
"Hide_Recipes": "Amagueu receptes",
|
||||
"Hide_as_header": "Amagueu com a títol",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Hora",
|
||||
"Hours": "Hores",
|
||||
"Icon": "Icona",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "Logotip",
|
||||
"Make_Header": "Establiu capçalera",
|
||||
"Make_Ingredient": "Establiu ingredient",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Gestioneu els llibres",
|
||||
"Manage_Emails": "Administrar correus",
|
||||
"Meal_Plan": "Pla d'àpats",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "El tipus és obligatori",
|
||||
"Meal_Types": "Tipus de menjars",
|
||||
"Merge": "Unificar",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Fusioneu paraula clau",
|
||||
"Message": "Missatge",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "Nova unitat",
|
||||
"Next_Day": "Següent dia",
|
||||
"Next_Period": "Període següent",
|
||||
"No": "",
|
||||
"NoCategory": "No s'ha seleccionat categoria.",
|
||||
"NoMoreUndo": "No hi ha canvis per desar.",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "Dia Anterior",
|
||||
"Previous_Period": "Període anterior",
|
||||
"Print": "Imprimir",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Recepta privada",
|
||||
"Private_Recipe_Help": "Només tu i la gent amb qui l'has compartit podran veure aquesta recepta.",
|
||||
"Properties": "Propietats",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "Receptes",
|
||||
"Recipes_In_Import": "Receptes al fitxer d'importació",
|
||||
"Recipes_per_page": "Receptes per pàgina",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Elimina {food} de la llista de la compra",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Esborreu nutrició de la recepta",
|
||||
"Reset": "Restablir",
|
||||
"Reset_Search": "Reinicieu la cerca",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "Mida",
|
||||
"Social_Authentication": "Identificació amb Xarxes Socials",
|
||||
"Sort_by_new": "Ordenar a partir del més nou",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Un administrador de l'espai podria canviar algunes configuracions estètiques i tindrien prioritat sobre la configuració dels usuaris per a aquest espai.",
|
||||
"Split_All_Steps": "Dividir totes les files en passos separats.",
|
||||
"StartDate": "Data d'inici",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Tipus",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Sense Canvis",
|
||||
"Undefined": "indefinit",
|
||||
"Undo": "Desfer",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "Vàlid fins",
|
||||
"View": "Mostrar",
|
||||
"View_Recipes": "Mostreu les receptes",
|
||||
"Visibility": "",
|
||||
"Waiting": "Esperant",
|
||||
"Warning": "Advertència",
|
||||
"Warning_Delete_Supermarket_Category": "Si suprimiu una categoria de supermercat, també se suprimiran totes les relacions alimentàries. N'estàs segur?",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "Números de la setmana",
|
||||
"Welcome": "Benvingut/da",
|
||||
"Year": "Any",
|
||||
"Yes": "",
|
||||
"add_keyword": "Afegir Paraula Clau",
|
||||
"additional_options": "Opcions addicionals",
|
||||
"advanced": "Avançat",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Účet",
|
||||
"Add": "Přidat",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Přidat {food} na váš nákupní seznam",
|
||||
"AddToShopping": "Přidat do nákupního seznamu",
|
||||
"Add_Servings_to_Shopping": "Přidat {servings} porce na nákupní seznam",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Automatizovat",
|
||||
"Automation": "Automatizace",
|
||||
"Back": "Zpět",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Skript v záložce",
|
||||
"Books": "Kuchařky",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Kalkulačka",
|
||||
"Calories": "Kalorie",
|
||||
"Cancel": "Zrušit",
|
||||
@@ -73,6 +81,7 @@
|
||||
"CustomNavLogoHelp": "Nahrajte obrázek, který se má zobrazit jako logo v navigačním panelu.",
|
||||
"CustomTheme": "Vlastní téma",
|
||||
"CustomThemeHelp": "Přepsat styly vybraného motivu nahráním vlastního souboru CSS.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Rozšiřte svůj prostor o seznamy potravin, jednotek a dalších položek spravovaných komunitou, a vylepšete tak svoji sbírku receptů.",
|
||||
"Datatype": "Datový typ",
|
||||
"Date": "Datum",
|
||||
@@ -85,6 +94,7 @@
|
||||
"DelayUntil": "Odložit do",
|
||||
"Delete": "Smazat",
|
||||
"DeleteShoppingConfirm": "Jste si jistí, že chcete odstranit všechno {food} z nákupního seznamu?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Smazat vše",
|
||||
"Delete_Food": "Smazat potravinu",
|
||||
"Delete_Keyword": "Smazat štítek",
|
||||
@@ -94,6 +104,7 @@
|
||||
"Disable_Amount": "Skrýt množství",
|
||||
"Disabled": "Deaktivované",
|
||||
"Documentation": "Dokumentace",
|
||||
"DontChange": "",
|
||||
"Download": "Stáhnout",
|
||||
"Drag_Here_To_Delete": "Přesunutím sem smazat",
|
||||
"Edit": "Upravit",
|
||||
@@ -113,10 +124,12 @@
|
||||
"Export_Supported": "Export podporován",
|
||||
"Export_To_ICal": "Export ovat .ics",
|
||||
"External": "Externí",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Externí obrázek receptu",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "ID v databázi FDC",
|
||||
"FDC_Search": "Vyhledávání v FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Selhání",
|
||||
"Fats": "Tuky",
|
||||
"File": "Soubor",
|
||||
@@ -139,6 +152,7 @@
|
||||
"Hide_Keywords": "Skrýt štítek",
|
||||
"Hide_Recipes": "Skrýt recept",
|
||||
"Hide_as_header": "Skryj jako nadpis",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Hodina",
|
||||
"Hours": "Hodiny",
|
||||
"Icon": "Ikona",
|
||||
@@ -186,6 +200,7 @@
|
||||
"Logo": "Logo",
|
||||
"Make_Header": "Použij jako nadpis",
|
||||
"Make_Ingredient": "Použij jako ingredienci",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Spravovat kuchařky",
|
||||
"Manage_Emails": "Spravovat emaily",
|
||||
"Meal_Plan": "Jídelníček",
|
||||
@@ -194,6 +209,7 @@
|
||||
"Meal_Type_Required": "Druh jídla je povinný",
|
||||
"Meal_Types": "Druhy jídel",
|
||||
"Merge": "Spojit",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Sloučit štítek",
|
||||
"Message": "Zpráva",
|
||||
"MissingProperties": "",
|
||||
@@ -224,6 +240,7 @@
|
||||
"New_Unit": "Nová jednotka",
|
||||
"Next_Day": "Následující den",
|
||||
"Next_Period": "Další období",
|
||||
"No": "",
|
||||
"NoCategory": "Není vybrána žádná kategorie.",
|
||||
"NoUnit": "",
|
||||
"No_ID": "ID nenalezeno, odstranění není možné.",
|
||||
@@ -263,6 +280,7 @@
|
||||
"Previous_Day": "Předchozí den",
|
||||
"Previous_Period": "Předchozí období",
|
||||
"Print": "Tisk",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Soukromý recept",
|
||||
"Private_Recipe_Help": "Recept můžete zobrazit pouze vy a lidé, se kterými jej sdílíte.",
|
||||
"Properties": "Nutriční vlastnosti",
|
||||
@@ -284,7 +302,9 @@
|
||||
"Recipes": "Recepty",
|
||||
"Recipes_In_Import": "Receptů v importním souboru",
|
||||
"Recipes_per_page": "Receptů na stránku",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Odstranit {food} z nákupního seznamu",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Smazat nutriční hodnoty",
|
||||
"Reset": "Resetovat",
|
||||
"Reset_Search": "Zrušit filtry vyhledávání",
|
||||
@@ -321,6 +341,7 @@
|
||||
"Size": "Velikost",
|
||||
"Social_Authentication": "Přihlašování pomocí účtů sociálních sítí",
|
||||
"Sort_by_new": "Seřadit od nejnovějšího",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Některá kosmetická nastavení mohou měnit správci prostoru a budou mít přednost před nastavením klienta pro daný prostor.",
|
||||
"Split_All_Steps": "Rozdělit každý řádek do samostatného kroku.",
|
||||
"StartDate": "Počáteční datum",
|
||||
@@ -351,6 +372,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Typ",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Neurčeno",
|
||||
"Unit": "Jednotka",
|
||||
"Unit_Alias": "Přezdívka jednotky",
|
||||
@@ -375,6 +397,7 @@
|
||||
"Valid Until": "Platné do",
|
||||
"View": "Zobrazit",
|
||||
"View_Recipes": "Zobrazit recepty",
|
||||
"Visibility": "",
|
||||
"Waiting": "Čekající",
|
||||
"Warning": "Varování",
|
||||
"Warning_Delete_Supermarket_Category": "Vymazáním kategorie obchodu dojde k odstranění všech vazeb na potraviny. Jste si jistí?",
|
||||
@@ -383,6 +406,7 @@
|
||||
"Week_Numbers": "Číslo týdne",
|
||||
"Welcome": "Vítejte",
|
||||
"Year": "Rok",
|
||||
"Yes": "",
|
||||
"add_keyword": "Přidat štítek",
|
||||
"additional_options": "Rozšířené možnosti",
|
||||
"advanced": "Pokročilé",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Bruger",
|
||||
"Add": "Tilføj",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Tilføj {food} til indkøbsliste",
|
||||
"AddToShopping": "Tilføj til indkøbsliste",
|
||||
"Add_Servings_to_Shopping": "Tilføj {servings} serveringer til indkøb",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Automatiser",
|
||||
"Automation": "Automatisering",
|
||||
"Back": "Tilbage",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Bogmærke",
|
||||
"Books": "Bøger",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Lommeregner",
|
||||
"Calories": "Kalorier",
|
||||
"Cancel": "Annuller",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "Upload et billede til brug som navigationsbarrelogo.",
|
||||
"CustomTheme": "Personaliseret tema",
|
||||
"CustomThemeHelp": "Overskriv det valgte temas stil ved at uploade en personlig CSS-fil.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Udbyg dit Space og gør din opskriftsamling bedre ved at importere en netværkskurateret liste af ingredienser, enheder og mere.",
|
||||
"Datatype": "Datatype",
|
||||
"Date": "Dato",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "Udskyd indtil",
|
||||
"Delete": "Slet",
|
||||
"DeleteShoppingConfirm": "Er du sikker på at du vil fjerne {food} fra indkøbsliste?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Slet alle",
|
||||
"Delete_Food": "Slet mad",
|
||||
"Delete_Keyword": "Slet nøgleord",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "Deaktiver antal",
|
||||
"Disabled": "Slået fra",
|
||||
"Documentation": "Dokumentation",
|
||||
"DontChange": "",
|
||||
"Download": "Download",
|
||||
"Drag_Here_To_Delete": "Træk herhen for at slette",
|
||||
"Edit": "Rediger",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "Eksport understøttet",
|
||||
"Export_To_ICal": "Eksporter .ics",
|
||||
"External": "Ekstern",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Eksternt billede af opskrift",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "FDC database ID",
|
||||
"FDC_Search": "FDC søgning",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Mislykkedes",
|
||||
"Fats": "Fedtstoffer",
|
||||
"File": "Fil",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "Skjul nøgleord",
|
||||
"Hide_Recipes": "Skjul opskrifter",
|
||||
"Hide_as_header": "Skjul som rubrik",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Time",
|
||||
"Hours": "Timer",
|
||||
"Icon": "Ikon",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "Logo",
|
||||
"Make_Header": "Opret rubrik",
|
||||
"Make_Ingredient": "Opret ingredient",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Administrer bøger",
|
||||
"Manage_Emails": "Håndter Emails",
|
||||
"Meal_Plan": "Madplan",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "Måltidstype påkrævet",
|
||||
"Meal_Types": "Måltidstyper",
|
||||
"Merge": "Sammenflet",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Sammenflet nøgleord",
|
||||
"Message": "Besked",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "Ny enhed",
|
||||
"Next_Day": "Næste dag",
|
||||
"Next_Period": "Næste periode",
|
||||
"No": "",
|
||||
"NoCategory": "Ingen kategori valgt.",
|
||||
"NoMoreUndo": "Ingen ændringer at fortryde.",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "Forgående dag",
|
||||
"Previous_Period": "Forgående periode",
|
||||
"Print": "Udskriv",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Privat opskrift",
|
||||
"Private_Recipe_Help": "Opskriften er kun synlig for dig, og dem som den er delt med.",
|
||||
"Properties": "Egenskaber",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "Opskrifter",
|
||||
"Recipes_In_Import": "Opskrifter i din importerede fil",
|
||||
"Recipes_per_page": "Opskrifter pr. side",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Fjern {food} fra indkøbsliste",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Fjern næringsindhold fra opskrift",
|
||||
"Reset": "Nulstil",
|
||||
"Reset_Search": "Nulstil søgning",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "Størrelse",
|
||||
"Social_Authentication": "Social authenticering",
|
||||
"Sort_by_new": "Sorter efter nylige",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Visse kosmetiske indstillinger kan ændres af område-administratorer og vil overskrive klient-indstillinger for pågældende område.",
|
||||
"Split_All_Steps": "Opdel rækker i separate trin.",
|
||||
"StartDate": "Startdato",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Type",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Uændret",
|
||||
"Undefined": "Ikke defineret",
|
||||
"Undo": "Fortryd",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "Gyldig indtil",
|
||||
"View": "Vis",
|
||||
"View_Recipes": "Vis opskrifter",
|
||||
"Visibility": "",
|
||||
"Waiting": "Vente",
|
||||
"Warning": "Advarsel",
|
||||
"Warning_Delete_Supermarket_Category": "At slette en supermarkedskategori vil også slette alle relationer til mad. Er du sikker?",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "Ugenumre",
|
||||
"Welcome": "Velkommen",
|
||||
"Year": "År",
|
||||
"Yes": "",
|
||||
"add_keyword": "Tilføj nøgleord",
|
||||
"additional_options": "Yderligere indstillinger",
|
||||
"advanced": "Avanceret",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Λογαριασμός",
|
||||
"Add": "Προσθήκη",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Προσθήκη του φαγητού {food} στη λίστα αγορών σας",
|
||||
"AddToShopping": "Προσθήκη στη λίστα αγορών",
|
||||
"Add_Servings_to_Shopping": "Προσθήκη {servings} μερίδων στις αγορές",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Αυτοματοποίηση",
|
||||
"Automation": "Αυτοματισμός",
|
||||
"Back": "Πίσω",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Bookmarklet",
|
||||
"Books": "Βιβλία",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Υπολογιστής",
|
||||
"Calories": "Θερμίδες",
|
||||
"Cancel": "Ακύρωση",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "Μεταφορτώστε μια εικόνα για χρήση ως λογότυπο της γραμμής πλοήγησης.",
|
||||
"CustomTheme": "Προσαρμοσμένο Θέμα",
|
||||
"CustomThemeHelp": "Αντικαταστήστε τα στυλ του επιλεγμένου θέματος μεταφορτώνοντας ένα προσαρμοσμένο αρχείο CSS.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Βελτιώστε τον χώρο και τη συλλογή συνταγών σας κάνοντας εισαγωγή μιας λίστας από φαγητά, μονάδες μέτρησης κ.α., επιμελημένη από την κοινότητα.",
|
||||
"Datatype": "Τύπος δεδομένων",
|
||||
"Date": "Ημερομηνία",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "Καθυστέρηση μέχρι",
|
||||
"Delete": "Διαγραφή",
|
||||
"DeleteShoppingConfirm": "Θέλετε σίγουρα να αφαιρέσετε τα {food} από τη λίστα αγορών;",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Διαγραφή όλων",
|
||||
"Delete_Food": "Διαγραφή φαγητού",
|
||||
"Delete_Keyword": "Διαγραφή λέξης-κλειδί",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "Απενεργοποίηση ποσότητας",
|
||||
"Disabled": "Απενεροποιημένο",
|
||||
"Documentation": "Τεκμηρίωση",
|
||||
"DontChange": "",
|
||||
"Download": "Λήψη",
|
||||
"Drag_Here_To_Delete": "Σύρετε εδώ για διαγραφή",
|
||||
"Edit": "Τροποποίηση",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "Υποστηρίζεται εξαγωγή",
|
||||
"Export_To_ICal": "Εξαγωγή .ics",
|
||||
"External": "Εξωτερική",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Εξωτερική εικόνα συνταγής",
|
||||
"FDC_ID": "Ταυτότητα FDC",
|
||||
"FDC_ID_help": "Ταυτότητα βάσης δεδομένων FDC",
|
||||
"FDC_Search": "Αναζήτηση FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Αποτυχία",
|
||||
"Fats": "Λιπαρά",
|
||||
"File": "Αρχείο",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "Απόκρυψη λέξης-κλειδί",
|
||||
"Hide_Recipes": "Απόκρυψη συνταγών",
|
||||
"Hide_as_header": "Απόκρυψη ως κεφαλίδα",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Ώρα",
|
||||
"Hours": "Ώρες",
|
||||
"Icon": "Εικονίδιο",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "Λογότυπο",
|
||||
"Make_Header": "Δημιουργία κεφαλίδας",
|
||||
"Make_Ingredient": "Δημιουργία υλικού",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Διαχείριση βιβλίων",
|
||||
"Manage_Emails": "Διαχείριση email",
|
||||
"Meal_Plan": "Πρόγραμμα γευμάτων",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "Το είδος του γεύματος είναι απαραίτητο",
|
||||
"Meal_Types": "Είδη γευμάτων",
|
||||
"Merge": "Συγχώνευση",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Συγχώνευση λέξης-κλειδί",
|
||||
"Message": "Μήνυμα",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "Νέα μονάδα μέτρησης",
|
||||
"Next_Day": "Επόμενη μέρα",
|
||||
"Next_Period": "Επόμενη περίοδος",
|
||||
"No": "",
|
||||
"NoCategory": "Δεν έχει επιλεγεί κατηγορία.",
|
||||
"NoMoreUndo": "Δεν υπάρχουν αλλαγές για ανέρεση.",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "Προηγούμενη μέρα",
|
||||
"Previous_Period": "Προηγούμενη περίοδος",
|
||||
"Print": "Εκτύπωση",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Ιδιωτική συνταγή",
|
||||
"Private_Recipe_Help": "Η συνταγή είναι ορατή μόνο σε εσάς και στα άτομα με τα οποία την μοιράζεστε.",
|
||||
"Properties": "Ιδιότητες",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "Συνταγές",
|
||||
"Recipes_In_Import": "Συνταγές στο αρχείο εισαγωγής",
|
||||
"Recipes_per_page": "Συνταγές ανά σελίδα",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Αφαίρεση του φαγητού {food} από τη λίστα αγορών σας",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Αφαίρεση διατροφικής αξίας από τη συνταγή",
|
||||
"Reset": "Επαναφορά",
|
||||
"Reset_Search": "Επαναφορά αναζήτησης",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "Μέγεθος",
|
||||
"Social_Authentication": "Ταυτοποίηση μέσω κοινωνικών δικτύων",
|
||||
"Sort_by_new": "Ταξινόμηση κατά νέο",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Ορισμένες ρυθμίσεις εμφάνισης μπορούν να αλλάξουν από τους διαχειριστές του χώρου και θα παρακάμψουν τις ρυθμίσεις πελάτη για αυτόν τον χώρο.",
|
||||
"Split_All_Steps": "Διαχωρισμός όλων των γραμμών σε χωριστά βήματα.",
|
||||
"StartDate": "Ημερομηνία Έναρξης",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Είδος",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Αμετάβλητο",
|
||||
"Undefined": "Απροσδιόριστο",
|
||||
"Undo": "Ανέρεση",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "Ισχύει έως",
|
||||
"View": "Προβολή",
|
||||
"View_Recipes": "Προβολή συνταγών",
|
||||
"Visibility": "",
|
||||
"Waiting": "Αναμονή",
|
||||
"Warning": "Προειδοποίηση",
|
||||
"Warning_Delete_Supermarket_Category": "Η διαγραφή μιας κατηγορίας supermarket θα διαγράψει και όλες τις σχέσεις της με φαγητά. Είστε σίγουροι;",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "Αριθμοί εδομάδων",
|
||||
"Welcome": "Καλώς ήρθατε",
|
||||
"Year": "Έτος",
|
||||
"Yes": "",
|
||||
"add_keyword": "Προσθήκη λέξης-κλειδί",
|
||||
"additional_options": "Επιπλέον επιλογές",
|
||||
"advanced": "Για προχωρημένους",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Tili",
|
||||
"Add": "Lisää",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Lisää {food} ostoslistaan",
|
||||
"AddToShopping": "Lisää ostoslistalle",
|
||||
"Add_Servings_to_Shopping": "Lisää {servings} Annoksia Ostoksiin",
|
||||
@@ -25,8 +28,13 @@
|
||||
"Automate": "Automatisoi",
|
||||
"Automation": "Automaatio",
|
||||
"Back": "Takaisin",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Kirjamerkki",
|
||||
"Books": "Kirjat",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Laskin",
|
||||
"Calories": "Kalorit",
|
||||
"Cancel": "Peruuta",
|
||||
@@ -71,6 +79,7 @@
|
||||
"CustomNavLogoHelp": "Lataa kuva käytettäväksi navigointipalkin logona.",
|
||||
"CustomTheme": "Mukautettu Teema",
|
||||
"CustomThemeHelp": "Ohita valitun teeman tyylit lataamalla mukautettu CSS-tiedosto.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Paranna tilaasi tuomalla yhteisön kuratoitu luettelo ruoista, yksiköistä ja muusta parantaaksesi reseptikokoelmaasi .",
|
||||
"Datatype": "Tietotyyppi",
|
||||
"Date": "Päivämäärä",
|
||||
@@ -83,6 +92,7 @@
|
||||
"DelayUntil": "Viive asti",
|
||||
"Delete": "Poista",
|
||||
"DeleteShoppingConfirm": "Oletko varma, että haluat poistaa kaikki {food} ostoslistalta?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Poista kaikki",
|
||||
"Delete_Food": "Poista ruoka",
|
||||
"Delete_Keyword": "Poista avainsana",
|
||||
@@ -92,6 +102,7 @@
|
||||
"Disable_Amount": "Poista Määrä käytöstä",
|
||||
"Disabled": "Ei käytössä",
|
||||
"Documentation": "Dokumentaatio",
|
||||
"DontChange": "",
|
||||
"Download": "Lataa",
|
||||
"Drag_Here_To_Delete": "Vedä tänne poistaaksesi",
|
||||
"Edit": "Muokkaa",
|
||||
@@ -111,10 +122,12 @@
|
||||
"Export_Supported": "Vienti tuettu",
|
||||
"Export_To_ICal": "Vie .ics",
|
||||
"External": "Ulkoinen",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Ulkoinen reseptin kuva",
|
||||
"FDC_ID": "FDC -tunnus",
|
||||
"FDC_ID_help": "FDC tietokanta tunnus",
|
||||
"FDC_Search": "FDC Haku",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Epäonnistui",
|
||||
"Fats": "Rasvat",
|
||||
"File": "Tiedosto",
|
||||
@@ -137,6 +150,7 @@
|
||||
"Hide_Keywords": "Piilota Avainsana",
|
||||
"Hide_Recipes": "Piilota Reseptit",
|
||||
"Hide_as_header": "Piilota otsikko",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Tunti",
|
||||
"Hours": "Tuntia",
|
||||
"Icon": "Kuvake",
|
||||
@@ -182,6 +196,7 @@
|
||||
"Logo": "Logo",
|
||||
"Make_Header": "Valmista Otsikko",
|
||||
"Make_Ingredient": "Valmista Ainesosa",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Hallinnoi kirjoja",
|
||||
"Manage_Emails": "Hallinnoi sähköposteja",
|
||||
"Meal_Plan": "Ateriasuunnitelma",
|
||||
@@ -190,6 +205,7 @@
|
||||
"Meal_Type_Required": "Ateriatyyppi pakollinen",
|
||||
"Meal_Types": "Ateriatyypit",
|
||||
"Merge": "Yhdistä",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Yhdistä Avainsana",
|
||||
"Message": "Viesti",
|
||||
"MissingProperties": "",
|
||||
@@ -215,6 +231,7 @@
|
||||
"New_Unit": "Uusi Yksikkö",
|
||||
"Next_Day": "Seuraava Päivä",
|
||||
"Next_Period": "Seuraava Jakso",
|
||||
"No": "",
|
||||
"NoCategory": "Luokkaa ei ole valittu.",
|
||||
"NoMoreUndo": "Ei peruttavia muutoksia.",
|
||||
"NoUnit": "",
|
||||
@@ -255,6 +272,7 @@
|
||||
"Previous_Day": "Edellinen Päivä",
|
||||
"Previous_Period": "Edellinen Jakso",
|
||||
"Print": "Tulosta",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Yksityinen Resepti",
|
||||
"Private_Recipe_Help": "Resepti näytetään vain sinulle ja ihmisille, joiden kanssa se jaetaan.",
|
||||
"Properties": "Ominaisuudet",
|
||||
@@ -276,7 +294,9 @@
|
||||
"Recipes": "Reseptit",
|
||||
"Recipes_In_Import": "Reseptit tuonti tiedostossasi",
|
||||
"Recipes_per_page": "Reseptejä sivulla",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Poista {food} ostoslistalta",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Poista ravintoaine reseptistä",
|
||||
"Reset": "Nollaa",
|
||||
"Reset_Search": "Nollaa haku",
|
||||
@@ -314,6 +334,7 @@
|
||||
"Size": "Koko",
|
||||
"Social_Authentication": "Sosiaalinen Todennus",
|
||||
"Sort_by_new": "Lajittele uusien mukaan",
|
||||
"Space": "",
|
||||
"Split_All_Steps": "Jaa kaikki rivit erillisiin vaiheisiin.",
|
||||
"StartDate": "Aloituspäivä",
|
||||
"Starting_Day": "Viikon aloituspäivä",
|
||||
@@ -338,6 +359,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Tyyppi",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Muuttumaton",
|
||||
"Undefined": "Määrittelemätön",
|
||||
"Undo": "Kumoa",
|
||||
@@ -364,6 +386,7 @@
|
||||
"Valid Until": "Voimassa Asti",
|
||||
"View": "Katso",
|
||||
"View_Recipes": "Näytä Reseptit",
|
||||
"Visibility": "",
|
||||
"Waiting": "Odottaa",
|
||||
"Warning": "Varoitus",
|
||||
"Website": "Verkkosivusto",
|
||||
@@ -371,6 +394,7 @@
|
||||
"Week_Numbers": "Viikkonumerot",
|
||||
"Welcome": "Tervetuloa",
|
||||
"Year": "Vuosi",
|
||||
"Yes": "",
|
||||
"add_keyword": "Lisää Avainsana",
|
||||
"additional_options": "Lisäasetukset",
|
||||
"advanced": "Edistynyt",
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
{
|
||||
"AI": "IA",
|
||||
"AIImportSubtitle": "Utiliser l'IA pour importer des images de recettes.",
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"AccessTokenHelp": "Clé d'accès pour l'API REST.",
|
||||
"Access_Token": "Jeton d'accès",
|
||||
"Account": "Compte",
|
||||
"Actions": "Actions",
|
||||
"Activity": "Activité",
|
||||
"Add": "Ajouter",
|
||||
"AddAll": "Tout ajouter",
|
||||
"AddChild": "",
|
||||
"AddFilter": "Ajouter un filtre",
|
||||
"AddFoodToShopping": "Ajouter l’aliment {food} à votre liste de courses",
|
||||
"AddMany": "Ajouter plusieurs",
|
||||
"AddToShopping": "Ajouter à la liste de courses",
|
||||
"Add_Servings_to_Shopping": "Ajouter {servings} portions aux courses",
|
||||
"Add_Step": "Ajouter une étape",
|
||||
@@ -12,31 +24,53 @@
|
||||
"Added_To_Shopping_List": "Ajouté à la liste de courses",
|
||||
"Added_by": "Ajouté par",
|
||||
"Added_on": "Ajouté le",
|
||||
"Admin": "Admin",
|
||||
"Advanced": "Avancé",
|
||||
"Advanced Search Settings": "Paramètres de recherche avancée",
|
||||
"Alignment": "Alignement",
|
||||
"AllRecipes": "Toutes les recettes",
|
||||
"Amount": "Quantité",
|
||||
"App": "Appli",
|
||||
"Apply": "",
|
||||
"Are_You_Sure": "Etes-vous sûr ?",
|
||||
"AppImportSubtitle": "Importer votre base de données de recettes existante.",
|
||||
"Apply": "Appliquer",
|
||||
"Are_You_Sure": "Êtes-vous sûr ?",
|
||||
"Auto_Planner": "Planning Auto",
|
||||
"Auto_Sort": "Tri automatique",
|
||||
"Auto_Sort_Help": "Déplacer tous les ingrédients à l’étape la mieux adaptée.",
|
||||
"Automate": "Automatiser",
|
||||
"Automation": "Automatisation",
|
||||
"AutomationHelp": "Les automatisations vous permettent, selon le type, d'appliquer certaines modifications automatiques aux recettes, aux ingrédients, ... par exemple lors de l'importation de recettes. ",
|
||||
"Available": "Disponible",
|
||||
"AvailableCategories": "Catégories disponibles",
|
||||
"Back": "Retour",
|
||||
"BaseUnit": "Unité de base",
|
||||
"BaseUnitHelp": "Unité standard pour la conversion automatique des unités",
|
||||
"Basics": "Les bases",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Book": "Livre",
|
||||
"Bookmarklet": "Signet",
|
||||
"BookmarkletHelp1": "Faites glisser le bouton suivant dans votre barre de signets",
|
||||
"BookmarkletHelp2": "Ouvrez la page à partir de laquelle vous souhaitez effectuer l'importation",
|
||||
"BookmarkletHelp3": "Cliquez sur le signet pour effectuer l'importation.",
|
||||
"BookmarkletImportSubtitle": "Utilisez un signet pour importer depuis des pages non publiques.",
|
||||
"Books": "Livres",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Calculatrice",
|
||||
"Calories": "Calories",
|
||||
"Cancel": "Annuler",
|
||||
"Cannot_Add_Notes_To_Shopping": "Les notes ne peuvent pas être ajoutées à la liste de courses",
|
||||
"Carbohydrates": "Glucides",
|
||||
"Cards": "Cartes",
|
||||
"Categories": "Catégories",
|
||||
"Category": "Catégorie",
|
||||
"CategoryInstruction": "Faites glisser les catégories pour modifier l'ordre dans lequel elles apparaissent dans la liste des courses.",
|
||||
"CategoryName": "Intitulé de la catégorie",
|
||||
"Change_Password": "Modifier le mot de passe",
|
||||
"ChildInheritFields": "Les enfants héritent des champs",
|
||||
"ChildInheritFields_help": "Les enfants hériteront de ces champs par défaut.",
|
||||
"Choose_Category": "Choisir une catégorie",
|
||||
"Clear": "Supprimer",
|
||||
"Click_To_Edit": "Cliquer pour éditer",
|
||||
@@ -45,9 +79,19 @@
|
||||
"Color": "Couleur",
|
||||
"Combine_All_Steps": "Combiner toutes les étapes en un seul champ.",
|
||||
"Coming_Soon": "Bientôt disponible",
|
||||
"Comment": "Commenter",
|
||||
"Comments_setting": "Montrer les commentaires",
|
||||
"Completed": "Achevé",
|
||||
"Confirm": "Confirmer",
|
||||
"ConnectorConfig": "Connecteurs",
|
||||
"ConnectorConfigHelp": "Avec les connecteurs, vous pouvez automatiquement synchroniser les données de Tandoor avec des services externes. ",
|
||||
"Continue": "Continuer",
|
||||
"Conversion": "Conversion",
|
||||
"ConversionsHelp": "Avec les conversions, vous pouvez calculer une quantité dans différentes unités. Actuellement, c'est utilisé uniquement pour le calcul des propriétés, mais ça pourrait être utilisé dans d'autres parties de Tandoor dans le futur. ",
|
||||
"CookLog": "Journal de cuisine",
|
||||
"CookLogHelp": "Entrées dans le journal de cuisine pour les recettes. ",
|
||||
"Cooked": "Cuit",
|
||||
"Copied": "Copié",
|
||||
"Copy": "Copier",
|
||||
"Copy Link": "Copier le lien",
|
||||
"Copy Token": "Copier le jeton",
|
||||
@@ -65,6 +109,8 @@
|
||||
"Create_New_Shopping_Category": "Ajouter une nouvelle catégorie de courses",
|
||||
"Create_New_Unit": "Ajouter une nouvelle unité",
|
||||
"Created": "Créé",
|
||||
"CreatedBy": "Créé par",
|
||||
"Ctrl+K": "Ctrl+K",
|
||||
"Current_Period": "Période actuelle",
|
||||
"Custom Filter": "Filtre personnalisé",
|
||||
"CustomImageHelp": "Téléchargez une image à afficher dans l'aperçu de l'espace.",
|
||||
@@ -73,83 +119,121 @@
|
||||
"CustomNavLogoHelp": "Téléchargez une image à utiliser comme logo de la barre de navigation.",
|
||||
"CustomTheme": "Thème personnalisé",
|
||||
"CustomThemeHelp": "Remplacer les styles du thème sélectionné en téléchargeant un fichier CSS personnalisé.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Améliorez votre groupe en important des données partagées par la communauté afin d'améliorer vos collections de recettes : listes d'aliments, unités et plus encore.",
|
||||
"Database": "Base de données",
|
||||
"DatabaseHelp": "Tandoor utilise beaucoup de mécanismes pour que vous puissiez créer des recettes, des listes de courses, des plans de repas et plus encore. C'est ici que vous pouvez gérer tous ces modèles.",
|
||||
"Datatype": "Type de donnée",
|
||||
"Date": "Date",
|
||||
"Day": "Jour",
|
||||
"Days": "Jours",
|
||||
"Decimals": "Décimales",
|
||||
"Default": "Par défaut",
|
||||
"DefaultPage": "Page par défaut",
|
||||
"Default_Unit": "Unité par défaut",
|
||||
"DelayFor": "Retard de {hours} heures",
|
||||
"DelayUntil": "Retard jusqu'à",
|
||||
"Delete": "Supprimer",
|
||||
"DeleteConfirmQuestion": "Voulez-vous vraiment supprimer cet objet ?",
|
||||
"DeleteShoppingConfirm": "Êtes-vous sûr(e) de vouloir supprimer tous les aliments {food} de votre liste de courses ?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Supprimer tout",
|
||||
"Delete_Food": "Supprimer l’aliment",
|
||||
"Delete_Keyword": "Supprimer le mot-clé",
|
||||
"Deleted": "Supprimé",
|
||||
"Description": "Description",
|
||||
"Description_Replace": "Remplacer la Description",
|
||||
"DeviceSettings": "Paramètres de l'appareil",
|
||||
"DeviceSettingsHelp": "Pour permettre Tandoor de paraître élégant de partout, ces paramètres sont uniquement stockés sur cet appareil.",
|
||||
"Disable": "Désactiver",
|
||||
"Disable_Amount": "Désactiver la quantité",
|
||||
"Disabled": "Désactivé",
|
||||
"Documentation": "Documentation",
|
||||
"DontChange": "",
|
||||
"Down": "Bas",
|
||||
"Download": "Télécharger",
|
||||
"DragToUpload": "Déplacer ou cliquer pour sélectionner",
|
||||
"Drag_Here_To_Delete": "Glissez ici pour supprimer",
|
||||
"Duplicate": "Dupliquer",
|
||||
"DuplicateFoundInfo": "Une recette avec cette URL existe déjà dans votre groupe. Continuer ?",
|
||||
"Edit": "Modifier",
|
||||
"Edit_Food": "Modifier l’aliment",
|
||||
"Edit_Keyword": "Modifier le mot-clé",
|
||||
"Edit_Meal_Plan_Entry": "Modifier une entrée de menu",
|
||||
"Edit_Recipe": "Modifier la recette",
|
||||
"Email": "Adresse email",
|
||||
"Empty": "Vider",
|
||||
"Enable": "Activer",
|
||||
"Enable_Amount": "Activer la quantité",
|
||||
"Enabled": "Activé",
|
||||
"EndDate": "Date de fin",
|
||||
"Energy": "Énergie",
|
||||
"Entries": "Entrées",
|
||||
"Error": "Erreur",
|
||||
"ErrorUrlListImport": "Une erreur est survenue lors de l'importation de la première URL de la liste. Les autres URLs n'apparaissant plus ont été importées avec succès. ",
|
||||
"Events": "Évènements",
|
||||
"Export": "Exporter",
|
||||
"Export_As_ICal": "Exporter la période en cours au format iCal",
|
||||
"Export_Not_Yet_Supported": "Exportation pas encore prise en charge",
|
||||
"Export_Supported": "Exportation prise en charge",
|
||||
"Export_To_ICal": "Exporter .ics",
|
||||
"External": "Externe",
|
||||
"ExternalRecipe": "",
|
||||
"ExternalRecipeImport": "Importation d'une recette externe",
|
||||
"ExternalRecipeImportHelp": "Les fichiers des dossiers synchronisés sur des stockages externes ne sont pas importés directement, mais enregistrés temporairement comme recettes d'importation externe. Vous pouvez ainsi visualiser et modifier rapidement les fichiers nouvellement trouvés avant leur transfert vers la collection principale. ",
|
||||
"ExternalStorage": "Stockage externe",
|
||||
"External_Recipe_Image": "Image de recette externe",
|
||||
"FDC_ID": "ID FCD",
|
||||
"FDC_ID_help": "ID de base de données FDC",
|
||||
"FDC_Search": "Recherche dans le FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Échec",
|
||||
"Fats": "Matières grasses",
|
||||
"File": "Fichier",
|
||||
"Files": "Fichiers",
|
||||
"FinishedAt": "Terminé à",
|
||||
"First": "Premier",
|
||||
"First_name": "Prénom",
|
||||
"Food": "Aliment",
|
||||
"FoodHelp": "Les aliments constituent la base essentielle du Tandoor. Avec leurs unités et leurs quantités respectives, ils constituent les ingrédients d'une recette. Ils peuvent également être utilisés pour les courses, les propriétés et bien plus encore. ",
|
||||
"FoodInherit": "Ingrédient hérité",
|
||||
"FoodNotOnHand": "L’aliment {food} n’est pas disponible.",
|
||||
"FoodOnHand": "L’aliment {food} est disponible.",
|
||||
"Food_Alias": "Alias pour les aliments",
|
||||
"Food_Replace": "Remplacer l'aliment",
|
||||
"Foods": "Aliments",
|
||||
"Fulltext": "",
|
||||
"FulltextHelp": "",
|
||||
"Fuzzy": "",
|
||||
"FuzzySearchHelp": "",
|
||||
"Friday": "Vendredi",
|
||||
"Fulltext": "Texte intégral",
|
||||
"FulltextHelp": "Champs de recherche en texte intégral. Remarque : les méthodes de recherche \"web\", \"phrase\" et \"raw\" ne fonctionnent qu'avec des champs en texte intégral.",
|
||||
"Fuzzy": "Approximatif",
|
||||
"FuzzySearchHelp": "Utilisez la recherche approximative pour trouver des entrées même lorsqu'il existe des différences dans la façon dont le mot est écrit.",
|
||||
"GettingStarted": "Commencer",
|
||||
"GroupBy": "Grouper par",
|
||||
"HeaderWarning": "Attention : Changer pour un En-tête supprimera la quantité / l'unité / l'aliment",
|
||||
"Headline": "En-tête",
|
||||
"Help": "Aide",
|
||||
"Hide_External": "Cacher les externes",
|
||||
"Hide_Food": "Cacher l’aliment",
|
||||
"Hide_Keyword": "masquer les mots clefs",
|
||||
"Hide_Keywords": "Cacher le mot-clé",
|
||||
"Hide_Recipes": "Cacher les recettes",
|
||||
"Hide_as_header": "Cacher comme en-tête",
|
||||
"Hierarchy": "",
|
||||
"History": "Historique",
|
||||
"HostedFreeVersion": "Vous utilisez la version gratuite de Tandoor",
|
||||
"Hour": "Heure",
|
||||
"Hours": "Heures",
|
||||
"Icon": "Icône",
|
||||
"IgnoreAccents": "",
|
||||
"IgnoreAccentsHelp": "",
|
||||
"IgnoreAccents": "Ignorer les accents",
|
||||
"IgnoreAccentsHelp": "Ignorez les accents lors de la recherche dans les champs donnés. ",
|
||||
"IgnoreThis": "Ne jamais ajouter automatiquement l’aliment {food} aux courses",
|
||||
"Ignore_Shopping": "Ignorer les courses",
|
||||
"IgnoredFood": "Ignorer les courses est paramétré pour l’aliment {food}.",
|
||||
"Image": "Image",
|
||||
"Import": "Importer",
|
||||
"Import Recipe": "Importer une recette",
|
||||
"ImportAll": "Tout importer",
|
||||
"ImportIntoTandoor": "Importer dans Tandoor",
|
||||
"Import_Error": "Une erreur est survenue pendant votre importation. Veuillez développer les détails au bas de la page pour la consulter.",
|
||||
"Import_Not_Yet_Supported": "Importation pas encore prise en charge",
|
||||
"Import_Result_Info": "{imported} sur {total} recettes ont été importées",
|
||||
@@ -159,8 +243,11 @@
|
||||
"Imported_From": "Importé depuis",
|
||||
"Importer_Help": "Plus d'information et d'aide sur cet importateur :",
|
||||
"Information": "Information",
|
||||
"Ingredient": "Ingrédient",
|
||||
"Ingredient Editor": "Éditeur d’ingrédients",
|
||||
"Ingredient Overview": "Aperçu des ingrédients",
|
||||
"IngredientEditorHelp": "Grâce à l'éditeur d'ingrédients, vous pouvez modifier simultanément tous les ingrédients utilisant un aliment et/ou une unité spécifique. Cela vous permet de corriger facilement les erreurs ou de modifier plusieurs recettes simultanément.",
|
||||
"IngredientHelp": "Les ingrédients se composent généralement d'une quantité, d'une unité et d'un aliment, la quantité et l'unité étant facultatives. Ils peuvent également contenir une note ou servir d'en-tête. ",
|
||||
"IngredientInShopping": "Cet ingrédient est dans votre liste de courses.",
|
||||
"Ingredients": "Ingrédients",
|
||||
"Inherit": "Hériter",
|
||||
@@ -170,37 +257,54 @@
|
||||
"Input": "Entrée",
|
||||
"Instruction_Replace": "Instruction Remplacer",
|
||||
"Instructions": "Instructions",
|
||||
"InstructionsEditHelp": "Cliquer ici pour ajouter des instructions. ",
|
||||
"Internal": "Interne",
|
||||
"InviteLinkHelp": "Liens pour inviter des nouvelles personnes dans votre groupe. ",
|
||||
"Invite_Link": "Lien d'invitation",
|
||||
"Invites": "Invitations",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"Key_Shift": "Maj",
|
||||
"Keyword": "Mot-clé",
|
||||
"KeywordHelp": "Les mots-clés peuvent être utilisés pour organiser votre collection de recettes.",
|
||||
"Keyword_Alias": "Alias de mot-clé",
|
||||
"Keywords": "Mots-clés",
|
||||
"Language": "Langue",
|
||||
"Last": "Dernier",
|
||||
"Last_name": "Nom",
|
||||
"Learn_More": "Apprenez-en plus",
|
||||
"Link": "Lien",
|
||||
"Load": "Chargement",
|
||||
"Load_More": "Charger plus",
|
||||
"Log_Cooking": "Marquer comme cuisiné",
|
||||
"Log_Recipe_Cooking": "Marquer la recette comme cuisinée",
|
||||
"Logo": "Logo",
|
||||
"Logout": "Déconnexion",
|
||||
"Make_Header": "Créer un en-tête",
|
||||
"Make_Ingredient": "Créer un ingrédient",
|
||||
"ManageSubscription": "Gérer l'abonnement",
|
||||
"Manage_Books": "Gérer les livres",
|
||||
"Manage_Emails": "Gérer les e-mails",
|
||||
"MealPlanHelp": "Un Plan de repas est une entrée de calendrier utilisée pour planifier des repas. Il doit contenir une recette ou un titre et peut être lié aux listes de courses. ",
|
||||
"Meal_Plan": "Menu de la semaine",
|
||||
"Meal_Plan_Days": "Futurs menus",
|
||||
"Meal_Type": "Type de repas",
|
||||
"Meal_Type_Required": "Type de repas obligatoire",
|
||||
"Meal_Types": "Types de repas",
|
||||
"Merge": "Fusionner",
|
||||
"MergeAutomateHelp": "Créer une automatisation pour remplacer les prochains objets de ce type par l'objet sélectionner.",
|
||||
"Merge_Keyword": "Fusionner le mot-clé",
|
||||
"Message": "Message",
|
||||
"MissingProperties": "",
|
||||
"Messages": "Messages",
|
||||
"Miscellaneous": "Autres",
|
||||
"MissingConversion": "Conversation manquante",
|
||||
"MissingProperties": "Propriétés manquantes",
|
||||
"ModelSelectResultsHelp": "Chercher plus de résultats",
|
||||
"Monday": "Lundi",
|
||||
"Month": "Mois",
|
||||
"More": "Plus",
|
||||
"Move": "Déplacer",
|
||||
"MoveCategory": "Déplacer vers : ",
|
||||
"MoveToStep": "Passer aux étapes",
|
||||
"Move_Down": "Descendre",
|
||||
"Move_Food": "Déplacer l’aliment",
|
||||
"Move_Keyword": "Déplacer le mot-clé",
|
||||
@@ -223,13 +327,17 @@
|
||||
"New_Supermarket": "Créer un nouveau supermarché",
|
||||
"New_Supermarket_Category": "Créer une nouvelle catégorie de supermarché",
|
||||
"New_Unit": "Nouvelle unité",
|
||||
"Next": "Suivant",
|
||||
"Next_Day": "Prochain jour",
|
||||
"Next_Period": "Prochaine période",
|
||||
"No": "",
|
||||
"NoCategory": "Pas de catégorie sélectionnée.",
|
||||
"NoMoreUndo": "Aucun changement à annuler.",
|
||||
"NoUnit": "",
|
||||
"NoUnit": "Pas d'unité",
|
||||
"No_ID": "ID introuvable, impossible de supprimer.",
|
||||
"No_Results": "Aucun résultat",
|
||||
"NotFound": "Introuvable",
|
||||
"NotFoundHelp": "La page ou l'objet que vous recherchez n'a pas pu être trouvé.",
|
||||
"NotInShopping": "L’aliment {food} n’est pas dans votre liste de courses.",
|
||||
"Note": "Notes",
|
||||
"Number of Objects": "Nombre d'objets",
|
||||
@@ -242,13 +350,18 @@
|
||||
"Open_Data_Import": "Import Open Data",
|
||||
"Open_Data_Slug": "Open Data Slug",
|
||||
"Options": "Options",
|
||||
"Order": "Ordre",
|
||||
"OrderInformation": "Les objects sont classés du plus petit au plus grand.",
|
||||
"Original_Text": "Texte d’origine",
|
||||
"Owner": "Propriétaire",
|
||||
"Page": "Page",
|
||||
"Parameter": "Paramètre",
|
||||
"Parent": "Parent",
|
||||
"PartialMatch": "",
|
||||
"PartialMatchHelp": "",
|
||||
"PartialMatch": "Correspondance partielle",
|
||||
"PartialMatchHelp": "Champs pour rechercher des correspondances partielles. (Par exemple, la recherche de « Tarte » renverra « tarte », « tartelette » et « entarté »)",
|
||||
"Password": "Mot de passe",
|
||||
"Path": "Chemin",
|
||||
"PerPage": "Par page",
|
||||
"Period": "Période",
|
||||
"Periods": "Périodes",
|
||||
"Pin": "Épingler",
|
||||
@@ -259,62 +372,103 @@
|
||||
"Planned": "Planifié",
|
||||
"Planner": "Planificateur",
|
||||
"Planner_Settings": "Paramètres du planificateur",
|
||||
"Planning&Shopping": "Planification et achats",
|
||||
"Plural": "Pluriel",
|
||||
"PrecisionSearchHelp": "",
|
||||
"Postpone": "Reporter",
|
||||
"PostponedUntil": "Reporté jusqu'à",
|
||||
"PrecisionSearchHelp": "Préréglage qui renvoie uniquement les entrées avec une orthographe correcte. ",
|
||||
"Preferences": "Préférences",
|
||||
"Preparation": "Préparation",
|
||||
"Preview": "Aperçu",
|
||||
"Previous_Day": "Jour précédent",
|
||||
"Previous_Period": "Période précédente",
|
||||
"Print": "Imprimer",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Recette privée",
|
||||
"Private_Recipe_Help": "La recette est uniquement visible par vous et les gens avec qui elle est partagée.",
|
||||
"Profile": "Profile",
|
||||
"Properties": "Propriétés",
|
||||
"PropertiesFoodHelp": "Des propriétés peuvent être ajoutées aux recettes et aux aliments. Les propriétés des aliments sont automatiquement calculées en fonction de leur quantité dans la recette.",
|
||||
"Properties_Food_Amount": "Propriété Quantité de nourriture",
|
||||
"Properties_Food_Unit": "Propriété Unité de nourriture",
|
||||
"Property": "Propriété",
|
||||
"PropertyHelp": "Combinaison du type de propriété, de la nourriture, de la recette et de la quantité",
|
||||
"PropertyType": "Type de propriété",
|
||||
"PropertyTypeHelp": "Les propriétés vous permettent de suivre différentes valeurs (nutrition, prix, …) pour des aliments individuels ou des recettes complètes. ",
|
||||
"Property_Editor": "Editeur de propriétés",
|
||||
"Protected": "Protégé",
|
||||
"Proteins": "Protéines",
|
||||
"Quick actions": "Actions Rapides",
|
||||
"QuickEntry": "Entrée rapide",
|
||||
"Random Recipes": "Recettes Aléatoires",
|
||||
"RandomOrder": "Ordre aléatoire",
|
||||
"RateLimit": "Fréquence limite",
|
||||
"RateLimitHelp": "Vous avez atteint la limite de requêtes pour le moment.",
|
||||
"Rating": "Note",
|
||||
"Ratings": "Notes",
|
||||
"Recently_Viewed": "Vu récemment",
|
||||
"Recipe": "Recette",
|
||||
"RecipeBookEntryHelp": "Les entrées du livre de recettes relient les recettes à des emplacements spécifiques dans les livres. ",
|
||||
"RecipeBookHelp": "Les livres de recettes contiennent des entrées de livre de recettes ou peuvent être automatiquement remplis à l'aide de filtres de recherche enregistrés. ",
|
||||
"RecipeHelp": "Les recettes sont la base de Tandoor et se composent d'informations générales et d'étapes, elles-mêmes composées d'ingrédients, d'instructions et plus encore. ",
|
||||
"RecipeStepsHelp": "Les ingrédients, les instructions et plus encore, peuvent être modifiés dans l'onglet Étapes.",
|
||||
"Recipe_Book": "Livre de recettes",
|
||||
"Recipe_Image": "Image de la recette",
|
||||
"Recipes": "Recettes",
|
||||
"Recipes_In_Import": "Recettes dans votre fichier d’importation",
|
||||
"Recipes_per_page": "Nombre de recettes par page",
|
||||
"Remove": "Enlever",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Supprimer l’aliment {food} de votre liste de courses",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Supprimer les valeurs nutritionelles de la recette",
|
||||
"Reset": "Réinitialiser",
|
||||
"ResetHelp": "Aide à la réinitialisation",
|
||||
"Reset_Search": "Réinitialiser la recherche",
|
||||
"Reusable": "Réutilisable",
|
||||
"Role": "Rôle",
|
||||
"Root": "Racine",
|
||||
"Saturday": "Samedi",
|
||||
"Save": "Sauvegarder",
|
||||
"Save_and_View": "Sauvegarder et visualiser",
|
||||
"Save/Load": "Enregistrer/Charger",
|
||||
"Save_and_View": "Enregistrer et visualiser",
|
||||
"SavedSearch": "Recherches enregistrées",
|
||||
"SavedSearchHelp": "Les recherches enregistrées peuvent être utilisées pour enregistrer des filtres de recherche afin de les récupérer facilement plus tard ou de remplir automatiquement des livres de recettes. ",
|
||||
"ScalableNumber": "Nombre évolutif",
|
||||
"Search": "Rechercher",
|
||||
"Search Settings": "Paramètres de recherche",
|
||||
"SearchMethod": "",
|
||||
"SearchSettingsOverview": "",
|
||||
"SearchSettingsWarning": "",
|
||||
"SearchMethod": "Méthode de recherche",
|
||||
"SearchSettingsOverview": "Choisissez l’un des préréglages recommandés ou ajustez vous-même les paramètres ci-dessous.",
|
||||
"SearchSettingsWarning": "Il n'est généralement pas nécessaire de modifier les paramètres de recherche. Ces paramètres sont réservés aux experts ayant des besoins spécifiques. ",
|
||||
"Second": "Seconde",
|
||||
"Seconds": "Secondes",
|
||||
"Select": "Sélectionner",
|
||||
"SelectAll": "Tout sélectionner",
|
||||
"SelectNone": "Sélection unique",
|
||||
"Select_App_To_Import": "Veuillez sélectionner une App pour importer depuis",
|
||||
"Select_Book": "Sélectionner le livre",
|
||||
"Select_File": "Sélectionner le fichier",
|
||||
"Selected": "Sélectionné",
|
||||
"SelectedCategories": "Catégories sélectionnées",
|
||||
"Serving": "Portion",
|
||||
"Servings": "Portions",
|
||||
"ServingsText": "Texte des portions",
|
||||
"Settings": "Paramètres",
|
||||
"Share": "Partager",
|
||||
"ShopLater": "Acheter plus tard",
|
||||
"ShopNow": "Acheter maintenant",
|
||||
"ShoppingBackgroundSyncWarning": "Mauvais réseau, en attente de synchronisation ...",
|
||||
"ShoppingListEntry": "Entrée de liste de courses",
|
||||
"ShoppingListEntryHelp": "Les entrées de liste de courses peuvent être créées manuellement ou via des recettes et des plans de repas.",
|
||||
"ShoppingListRecipe": "Recette de liste de courses",
|
||||
"Shopping_Categories": "Catégories de courses",
|
||||
"Shopping_Category": "Catégorie de courses",
|
||||
"Shopping_List_Empty": "Votre liste de courses est actuellement vide, vous pouvez ajouter des articles via le menu contextuel d’une entrée de menu de la semaine (clic droit sur la carte ou clic gauche sur l’icône du menu)",
|
||||
"Shopping_input_placeholder": "par ex. Pommes de terre/100 Pommes de terre/100 gr Pomme de terre",
|
||||
"Shopping_list": "Liste de courses",
|
||||
"ShowDelayed": "Afficher les éléments retardés",
|
||||
"ShowIngredients": "Montrer les ingrédients",
|
||||
"ShowMealPlanOnStartPage": "Afficher les plans de repas sur la page de démarrage.",
|
||||
"ShowRecentlyCompleted": "Afficher les éléments récemment complétés",
|
||||
"ShowUncategorizedFood": "Montrer ce qui est indéfini",
|
||||
"Show_Logo": "Montrer le logo",
|
||||
@@ -325,47 +479,93 @@
|
||||
"Size": "Taille",
|
||||
"Social_Authentication": "Authentification Sociale",
|
||||
"Sort_by_new": "Trier par nouveautés",
|
||||
"Source": "Source",
|
||||
"SourceImportHelp": "Importez du JSON au format schema.org/recipe ou des pages HTML avec une recette json+ld ou des microdonnées.",
|
||||
"SourceImportSubtitle": "Importez en JSON ou HTML manuellement.",
|
||||
"Space": "",
|
||||
"SpaceLimitExceeded": "Votre groupe a dépassé une de ses limites, certaines fonctions pourraient être restreintes.",
|
||||
"SpaceLimitReached": "Ce groupe a atteint sa limite. Aucun nouvel objet de ce type ne peut être créé.",
|
||||
"SpaceMemberHelp": "Ajoutez des utilisateurs à votre espace en créant un lien d'invitation et en l'envoyant à la personne que vous souhaitez ajouter.",
|
||||
"SpaceMembers": "Membres du groupe",
|
||||
"SpaceMembersHelp": "Utilisateurs et permissions dans un groupe. ",
|
||||
"SpaceSettings": "Paramètres du groupe",
|
||||
"Space_Cosmetic_Settings": "Certains paramètres cosmétiques peuvent être modifiés par un administrateur de l'espace et seront prioritaires sur les paramètres des utilisateurs pour cet espace.",
|
||||
"Split": "Diviser",
|
||||
"Split_All_Steps": "Diviser toutes les lignes en étapes séparées.",
|
||||
"StartDate": "Date de début",
|
||||
"Starting_Day": "Jour de début de la semaine",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
"StartsWith": "Commence par",
|
||||
"StartsWithHelp": "Champs dans lesquels les débuts de mots correspondants sont recherchés. (Par exemple, une recherche pour « po » renverra « pomme » et « poireau ».)",
|
||||
"Step": "Étape",
|
||||
"StepHelp": "Les étapes contiennent des ingrédients (constitués de quantité / unité / aliment), des instructions, des images et plus d'informations sur cette étape dans la recette. ",
|
||||
"Step_Name": "Nom de l’étape",
|
||||
"Step_Type": "Type d’étape",
|
||||
"Step_start_time": "Heure de début de l’étape",
|
||||
"Steps": "Étapes",
|
||||
"StepsOverview": "Aperçu des étapes",
|
||||
"Sticky_Nav": "Barre de navigation collante",
|
||||
"Sticky_Nav_Help": "Toujours afficher le menu de navigation en haut de l’écran.",
|
||||
"Storage": "Stockage externe",
|
||||
"StorageHelp": "Emplacements de stockage externes où les fichiers de recettes (images / PDF) peuvent être stockés et synchronisés avec Tandoor.",
|
||||
"StoragePasswordTokenHelp": "Le mot de passe / jeton enregistré ne sera jamais affiché. Il ne sera modifié que si un nouveau mot de passe est saisi dans le champ. ",
|
||||
"SubstituteOnHand": "Vous avez une alternative sous la main.",
|
||||
"Substitutes": "Alternatives",
|
||||
"Success": "Réussite",
|
||||
"SuccessClipboard": "Liste de courses copiée dans le presse-papiers",
|
||||
"Sunday": "Dimanche",
|
||||
"Supermarket": "Supermarché",
|
||||
"SupermarketCategoriesOnly": "Catégories de supermarché uniquement",
|
||||
"SupermarketCategoryHelp": "Les catégories décrivent les zones dans les supermarchés (par exemple, Fruits et légumes, Épicerie fine, Charcuterie …). Elles peuvent être liés aux aliments et aux supermarchés pour automatiser le tri.",
|
||||
"SupermarketHelp": "Avec les supermarchés, vous pouvez lier des catégories pour trier et filtrer automatiquement les listes de courses. ",
|
||||
"SupermarketName": "Nom du supermarché",
|
||||
"Supermarkets": "Supermarchés",
|
||||
"SupportsDescriptionField": "Supporte le champ Description",
|
||||
"SyncLog": "Journal de synchronisation",
|
||||
"SyncLogHelp": "Protocole pour la synchronisation des recettes externes.",
|
||||
"SyncedPath": "Dossier synchronisé",
|
||||
"SyncedPathHelp": "Dossiers sur des emplacements de stockage externes qui sont surveillés. ",
|
||||
"System": "Système",
|
||||
"Table": "Tableau",
|
||||
"Table_of_Contents": "Table des Matières",
|
||||
"Text": "Texte",
|
||||
"ThankYou": "Merci",
|
||||
"ThanksTextHosted": "Pour soutenir l'open source en utilisant le serveur officiel Tandoor.",
|
||||
"ThanksTextSelfhosted": "Pour utiliser Tandoor, si vous souhaitez soutenir le développement futur, pensez à sponsoriser le projet via GitHub.",
|
||||
"Theme": "Thème",
|
||||
"Thursday": "Jeudi",
|
||||
"Time": "Temps",
|
||||
"Title": "Titre",
|
||||
"Title_or_Recipe_Required": "Sélection du titre ou de la recette requise",
|
||||
"Today": "Aujoud'hui",
|
||||
"Toggle": "Basculer",
|
||||
"Transpose_Words": "Transposer les mots",
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"TrigramThreshold": "Seuil du trigramme",
|
||||
"TrigramThresholdHelp": "Contrôle le nombre de fautes d'orthographe ignorées lors de la recherche approximative. Les valeurs faibles ignorent davantage de différences.",
|
||||
"Tuesday": "Mardi",
|
||||
"Type": "Type",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Inchangé",
|
||||
"Undefined": "Indéfini",
|
||||
"Undo": "annuler",
|
||||
"Unit": "Unité",
|
||||
"UnitConversion": "Conversion d'unités",
|
||||
"UnitConversionHelp": "La conversion d'unités vous permet de convertir des unités unique, de manière générale ou pour un aliment spécifique. Vous pouvez par exemple convertir 1 tasse de farine en 125 grammes. Tandoor effectue ensuite automatiquement la conversion dans les différentes unités de poids ou de volume, si celles-ci ont les bonnes unités de base. Les conversions d'unités sont utilisées pour les calculs de propriétés.",
|
||||
"UnitHelp": "Les unités, les aliments et les quantités constituent les ingrédients du recette. Vous pouvez les nommer selon vos préférences et les associer à des unités standardisées pour une conversion automatique. De plus, ils donnent du contexte aux quantités dans de nombreux contextes, comme les listes de courses, les conversions et les propriétés. ",
|
||||
"Unit_Alias": "Alias pour les unités",
|
||||
"Unit_Replace": "Remplacer l'Unité",
|
||||
"Units": "Unités",
|
||||
"Unpin": "Détacher",
|
||||
"UnpinnedConfirmation": "{recipe} a été désépinglée.",
|
||||
"Unrated": "Non évalué",
|
||||
"Up": "Haut",
|
||||
"Update": "Mettre à jour",
|
||||
"Update_Existing_Data": "Mettre à jour les données existantes",
|
||||
"Updated": "Mis à jour",
|
||||
"UpgradeNow": "Mettre à niveau maintenant",
|
||||
"Url": "URL",
|
||||
"UrlImportSubtitle": "Importez des recettes à partir de milliers de pages prises en charge.",
|
||||
"UrlList": "Liste d'URLs",
|
||||
"UrlListSubtitle": "Importer automatiquement une liste d'URLs.",
|
||||
"Url_Import": "Importation de l’url",
|
||||
"Use_Fractions": "Utiliser les fractions",
|
||||
"Use_Fractions_Help": "Convertir les décimales en fractions automatiquement lors de la visualisation d’une recette.",
|
||||
@@ -376,30 +576,47 @@
|
||||
"Use_Plural_Unit_Always": "Toujours utiliser la forme plurielle pour les unités",
|
||||
"Use_Plural_Unit_Simple": "Utiliser la forme plurielle pour les unités de manière dynamique",
|
||||
"User": "Utilisateur",
|
||||
"UserFileHelp": "Fichiers téléchargés au groupe. ",
|
||||
"UserHelp": "Les utilisateurs sont les membres de votre groupe. ",
|
||||
"Username": "Nom d’utilisateur",
|
||||
"Users": "Utilisateurs",
|
||||
"Valid Until": "Valide jusqu’au",
|
||||
"View": "Voir",
|
||||
"ViewLogHelp": "Historique des recettes consultées. ",
|
||||
"View_Recipes": "Voir les recettes",
|
||||
"Viewed": "Vue",
|
||||
"Visibility": "",
|
||||
"Waiting": "Attente",
|
||||
"WaitingTime": "Temps d'attente",
|
||||
"WarnPageLeave": "Certaines modifications non enregistrées seront perdues. Voulez-vous quand même quitter la page ?",
|
||||
"Warning": "Avertissement",
|
||||
"WarningRecipeBookEntryDuplicate": "Une recette ne peut être ajoutée à un livre qu'une seule fois.",
|
||||
"Warning_Delete_Supermarket_Category": "Supprimer une catégorie de supermarché supprimera également toutes les relations avec les aliments. Êtes-vous sûr ?",
|
||||
"Website": "Site",
|
||||
"Wednesday": "Mercredi",
|
||||
"Week": "Semaine",
|
||||
"Week_Numbers": "Numéro de semaine",
|
||||
"Welcome": "Bienvenue",
|
||||
"WorkingTime": "Temps de préparation",
|
||||
"Year": "Année",
|
||||
"Yes": "",
|
||||
"YourSpaces": "Vos groupes",
|
||||
"active": "actif",
|
||||
"add_keyword": "Ajouter un Mot Clé",
|
||||
"additional_options": "Options Supplémentaires",
|
||||
"advanced": "Avancé",
|
||||
"advanced_search_settings": "Paramètres de recherche avancée",
|
||||
"after": "après",
|
||||
"all": "tout",
|
||||
"all_fields_optional": "Tous les champs sont facultatifs et peuvent être laissés vides.",
|
||||
"and": "et",
|
||||
"and_down": "& Dessous",
|
||||
"and_up": "&Au-dessus",
|
||||
"any": "n'importe",
|
||||
"asc": "Ordre croissant",
|
||||
"base_amount": "Quantité de base",
|
||||
"base_unit": "Unités de base",
|
||||
"before": "avant",
|
||||
"book_filter_help": "Inclure les recettes filtrées en plus de celles ajoutées manuellement.",
|
||||
"click_image_import": "Cliquez sur l'image que vous souhaitez importer pour cette recette",
|
||||
"confirm_delete": "Voulez-vous vraiment supprimer {objet} ?",
|
||||
@@ -442,6 +659,8 @@
|
||||
"err_move_self": "Impossible de déplacer un élément vers lui-même",
|
||||
"err_moving_resource": "Erreur lors du déplacement d’une ressource !",
|
||||
"err_updating_resource": "Erreur lors de la mise à jour d’une ressource !",
|
||||
"exact": "exacte",
|
||||
"exclude": "exclure",
|
||||
"expert_mode": "Mode expert",
|
||||
"explain": "Expliquer",
|
||||
"fields": "Champs",
|
||||
@@ -450,11 +669,20 @@
|
||||
"filter_name": "Filtrer par nom",
|
||||
"filter_to_supermarket": "Limiter au supermarché",
|
||||
"filter_to_supermarket_desc": "Par défaut, la liste de courses est filtrée pour n'inclure que les catégories du supermarché sélectionné.",
|
||||
"fluid_ounce": "once liquide [fl oz] (US, volume)",
|
||||
"food_inherit_info": "Champs sur les aliments à hériter par défaut.",
|
||||
"food_recipe_help": "Ajouter un lien vers la recette ici incluera cette recette dans n'importe qu'elle autre recette qui utilise cet ingrédient",
|
||||
"g": "gramme [g] (métrique, poids)",
|
||||
"gallon": "gallon [gal] (US, volume)",
|
||||
"hide_step_ingredients": "Cacher les ingrédients de l'étape",
|
||||
"hours": "heures",
|
||||
"ignore_shopping_help": "Ne jamais ajouter d’aliment à la liste de courses (ex. : eau)",
|
||||
"imperial_fluid_ounce": "once liquide impériale [imp fl oz] (Royaume-Uni, volume)",
|
||||
"imperial_gallon": "gal impériale [gal imp.] (Royaume-Uni, volume)",
|
||||
"imperial_pint": "pinte impériale [pt imp.] (Royaume-Uni, volume)",
|
||||
"imperial_quart": "quart impérial [imp qt] (Royaume-Uni, volume)",
|
||||
"imperial_tbsp": "cuillère à soupe impériale [imp c. à soupe] (Royaume-Uni, volume)",
|
||||
"imperial_tsp": "cuillère à thé impériale [imp c. à thé] (Royaume-Uni, volume)",
|
||||
"import_duplicates": "Pour éviter les doublons, les recettes de même nom seront ignorées. Cocher la case pour tout importer.",
|
||||
"import_running": "Importation en cours, veuillez patienter !",
|
||||
"in_shopping": "Dans la liste de courses",
|
||||
@@ -487,6 +715,7 @@
|
||||
"not": "pas",
|
||||
"nothing": "Rien à effectuer",
|
||||
"nothing_planned_today": "Vous n'avez rien de prévu pour aujourd'hui !",
|
||||
"on": "sur",
|
||||
"one_url_per_line": "Une URL par ligne",
|
||||
"open_data_help_text": "Le projet «Tandoor Open Data» est une base de données fournie par la communauté. Ce champ est rempli automatiquement lors de l'importation des données et permet les mises à jour dans le futur.",
|
||||
"or": "ou",
|
||||
@@ -496,11 +725,13 @@
|
||||
"paste_ingredients_placeholder": "Copier la liste d'ingrédients ici...",
|
||||
"paste_json": "Collez une source json ou html pour charger la recette.",
|
||||
"per_serving": "par portions",
|
||||
"pint": "pinte [pt] (US, volume)",
|
||||
"plan_share_desc": "Les nouvelles entrées de menu de la semaine seront partagées automatiquement avec des utilisateurs sélectionnés.",
|
||||
"plural_short": "pluriel",
|
||||
"plural_usage_info": "Utiliser la forme plurielle pour les unités et les aliments dans ce groupe.",
|
||||
"pound": "livre (poids)",
|
||||
"property_type_fdc_hint": "Seules les propriétés avec un ID FDC peuvent être mises à jour automatiquement depuis la base FDC",
|
||||
"quart": "quart [qt] (US, volume)",
|
||||
"recipe_filter": "Filtrer les recettes",
|
||||
"recipe_name": "Nom de la recette",
|
||||
"recipe_property_info": "Vous pouvez également ajouter des propriétés aux aliments pour les calculer automatiquement en fonction de votre recette !",
|
||||
@@ -509,15 +740,22 @@
|
||||
"remember_search": "Enregistrer la recherche",
|
||||
"remove_selection": "Désélectionner",
|
||||
"reset_children": "Réinitialiser l'héritage enfant",
|
||||
"reset_children_help": "Remplacer tous les enfants par les valeurs des champs hérités. Les champs hérités des enfants seront définis sur « Champs hérités », sauf si « Champs hérités des enfants » est défini.",
|
||||
"reset_food_inheritance": "Réinitialiser l'héritage",
|
||||
"reset_food_inheritance_info": "Réinitialiser tous les champs d'héritage des aliments par les valeurs de leurs parents.",
|
||||
"reusable_help_text": "Le lien d’invitation doit-il être utilisable par plus d’un utilisateur.",
|
||||
"review_shopping": "Vérifier les éléments de la liste avant de sauvegarder",
|
||||
"save_filter": "Sauvegarder le filtre",
|
||||
"searchFilterCreatedByHelp": "Recettes créées par l'utilisateur sélectionné.",
|
||||
"searchFilterObjectsAndHelp": "Recettes avec tous les {type} sélectionnés",
|
||||
"searchFilterObjectsAndNotHelp": "Exclure les recettes avec tous les {type} sélectionnés",
|
||||
"searchFilterObjectsHelp": "Recettes avec l'un des {type} sélectionnés",
|
||||
"searchFilterObjectsOrNotHelp": "Seules les recettes où tous les aliments (ou leurs alternatives) sont marqués comme étant sous la main.",
|
||||
"search_create_help_text": "Créer une nouvelle recette directement dans Tandoor.",
|
||||
"search_import_help_text": "Importer une recette depuis un site ou une application externe.",
|
||||
"search_no_recipes": "Aucune recette trouvée !",
|
||||
"search_rank": "Rechercher par note",
|
||||
"seconds": "secondes",
|
||||
"select_file": "Sélectionner Fichier",
|
||||
"select_food": "Sélectionner l’aliment",
|
||||
"select_keyword": "Sélectionner Mot Clé",
|
||||
@@ -552,8 +790,10 @@
|
||||
"sort_by": "Trié par",
|
||||
"sql_debug": "Débogage de la base SQL",
|
||||
"step_time_minutes": "Temps passé en minutes",
|
||||
"substitute_children": "Enfants alternatifs",
|
||||
"substitute_children_help": "Tout aliment étant enfant de cet aliment est considéré comme substitut.",
|
||||
"substitute_help": "Les substituts sont pris en compte lors d'une recherche de recette pouvant être cuisinée avec les ingrédients disponibles.",
|
||||
"substitute_siblings": "Frères et sœurs alternatifs",
|
||||
"substitute_siblings_help": "Tous les aliments qui partagent un parent avec cet aliment sont considérés comme des substituts.",
|
||||
"success_creating_resource": "Ressource créée avec succès !",
|
||||
"success_deleting_resource": "Ressource supprimée avec succès !",
|
||||
@@ -561,12 +801,18 @@
|
||||
"success_merging_resource": "Ressource fusionnée avec succès !",
|
||||
"success_moving_resource": "Ressource déplacée avec succès !",
|
||||
"success_updating_resource": "Ressource mise à jour avec succès !",
|
||||
"tbsp": "",
|
||||
"tbsp": "cuillère à soupe [tbsp] (US, volume)",
|
||||
"theUsernameCannotBeChanged": "Le nom d'utilisateur ne peut pas être modifié.",
|
||||
"times_cooked": "Nombre de fois cuisiné",
|
||||
"to_close": "pour fermer",
|
||||
"to_navigate": "pour naviguer",
|
||||
"to_select": "pour sélectionner",
|
||||
"today_recipes": "Recettes du jour",
|
||||
"total": "total",
|
||||
"tree_root": "Racine de l’arbre",
|
||||
"tree_select": "Utiliser l'arbre de sélection",
|
||||
"tsp": "cuillère à thé [tsp] (US, volume)",
|
||||
"unsaved": "non enregistré",
|
||||
"updatedon": "Mis à jour le",
|
||||
"view_recipe": "Voir la recette",
|
||||
"warning_duplicate_filter": "Attention : en raison de limitations techniques, l'emploi de multiples filtres (and/or/not) peut mener à des résultats inattendus.",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "חשבון",
|
||||
"Add": "הוספה",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "הוסף {מזון} לרשימת הקניות",
|
||||
"AddToShopping": "הוסף לרשימת קניות",
|
||||
"Add_Servings_to_Shopping": "הוסף{מנה}מנות לקנייה",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "אוטומט",
|
||||
"Automation": "אוטומטציה",
|
||||
"Back": "חזור",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "סימניה",
|
||||
"Books": "ספרים",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "מחשבון",
|
||||
"Calories": "קלוריות",
|
||||
"Cancel": "ביטול",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "העלאת תמונה שתשמש כתמונה באזור הניווט.",
|
||||
"CustomTheme": "ערכת נושא מותאמת אישית",
|
||||
"CustomThemeHelp": "העלאה של קובץ CSS מותאם אישית תדרוס את העיצוב של הערכת נושא שנבחרה.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "שפר את המרחב שלך ע\"י ייבוא רשימת משאבים קהילתית כמו מאכלים, ערכים ועוד.",
|
||||
"Datatype": "סוג מידע",
|
||||
"Date": "תאריך",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "השהה עד",
|
||||
"Delete": "מחק",
|
||||
"DeleteShoppingConfirm": "האם אתה בטוח שברצונך להסיר את כל ה{מזון} מרשימת הקניות ?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "מחק הכל",
|
||||
"Delete_Food": "מחק אוכל",
|
||||
"Delete_Keyword": "מחר מילת מפתח",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "אל תאפשר כמות",
|
||||
"Disabled": "מושבת",
|
||||
"Documentation": "תיעוד",
|
||||
"DontChange": "",
|
||||
"Download": "הורדה",
|
||||
"Drag_Here_To_Delete": "משוך לכאן למחיקה",
|
||||
"Edit": "ערוך",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "ייצוא נתמך",
|
||||
"Export_To_ICal": "ייצא .ics",
|
||||
"External": "חיצוני",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "תמונת מתכון חיצונית",
|
||||
"FDC_ID": "מספר FDC",
|
||||
"FDC_ID_help": "מספר FDC",
|
||||
"FDC_Search": "חפש FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "כשלון",
|
||||
"Fats": "שומנים",
|
||||
"File": "קובץ",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "הסתרת מילת מפתח",
|
||||
"Hide_Recipes": "הסתרת מתכונים",
|
||||
"Hide_as_header": "הסתר בתור כותרת",
|
||||
"Hierarchy": "",
|
||||
"Hour": "שעה",
|
||||
"Hours": "שעות",
|
||||
"Icon": "צלמית",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "לוגו",
|
||||
"Make_Header": "הפוך לכותרת",
|
||||
"Make_Ingredient": "הפוך למרכיב",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "נהל ספרים",
|
||||
"Manage_Emails": "נהל כתובות דואר אלקטרוני",
|
||||
"Meal_Plan": "תוכנית ארוחה",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "סוג אוכל נדרש",
|
||||
"Meal_Types": "סוגי אוכל",
|
||||
"Merge": "איחוד",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "איחוד מילת מפתח",
|
||||
"Message": "הודעה",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "יחידה חדשה",
|
||||
"Next_Day": "היום הבא",
|
||||
"Next_Period": "התקופה הבאה",
|
||||
"No": "",
|
||||
"NoCategory": "לא נבחרה קטגוריה.",
|
||||
"NoMoreUndo": "אין עוד שינויים לשחזור.",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "יום קודם",
|
||||
"Previous_Period": "תקופה קודמת",
|
||||
"Print": "הדפסה",
|
||||
"Private": "",
|
||||
"Private_Recipe": "מתכון פרטי",
|
||||
"Private_Recipe_Help": "המתכון מוצג רק לך ולאנשים ששותפו.",
|
||||
"Properties": "ערכים",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "מתכונים",
|
||||
"Recipes_In_Import": "מתכון בקובץ הייבוא",
|
||||
"Recipes_per_page": "מתכונים בכל דף",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "הסר {מזון} מרשימת הקניות",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "מחר ערכים תזונתיים מהמתכון",
|
||||
"Reset": "אפס",
|
||||
"Reset_Search": "אפס חיפוש",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "גודל",
|
||||
"Social_Authentication": "אימות חברתי",
|
||||
"Sort_by_new": "סדר ע\"י חדש",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "חלק מהגדרות הקוסמטיות יכולות להיות מעודכנות על ידי מנהל המרחב וידרסו את הגדרות הקליינט עבור מרחב זה.",
|
||||
"Split_All_Steps": "פצל את כל השורות לצעדים נפרדים.",
|
||||
"StartDate": "תאריך התחלה",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "סוג",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "ללא שינוי",
|
||||
"Undefined": "בלתי מוגדר",
|
||||
"Undo": "שחזר",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "פעיל עד",
|
||||
"View": "תצוגה",
|
||||
"View_Recipes": "הצג מתכונים",
|
||||
"Visibility": "",
|
||||
"Waiting": "המתנה",
|
||||
"Warning": "אזהרה",
|
||||
"Warning_Delete_Supermarket_Category": "מחיקת קטגורית סופרמרקט תמחוק גם את המאכלים הקשורים. האם אתה בטוח ?",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "מספר השבוע",
|
||||
"Welcome": "ברוכים הבאים",
|
||||
"Year": "שנה",
|
||||
"Yes": "",
|
||||
"add_keyword": "הוסף מילת מפתח",
|
||||
"additional_options": "אפשרויות נוספות",
|
||||
"advanced": "מתקדם",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Korisnički račun",
|
||||
"Add": "Dodaj",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Dodaj {food} na svoj popis za kupovinu",
|
||||
"AddToShopping": "Dodaj na popis za kupovinu",
|
||||
"Add_Servings_to_Shopping": "Dodaj {servings} obroka u Kupovinu",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Automatiziraj",
|
||||
"Automation": "Automatizacija",
|
||||
"Back": "Nazad",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Knjižna oznaka",
|
||||
"Books": "Knjige",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Kalkulator",
|
||||
"Calories": "Kalorije",
|
||||
"Cancel": "Otkaži",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "Učitaj sliku koju ćeš koristiti kao logotip navigacijske trake.",
|
||||
"CustomTheme": "Prilagođena tema",
|
||||
"CustomThemeHelp": "Nadjačaj stilove odabrane teme učitavanjem prilagođene CSS datoteke.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Unaprijedite svoj prostor uvozom popisa namirnica, jedinica i još mnogo toga koje je pripremila zajednica kako biste poboljšali svoju kolekciju recepata.",
|
||||
"Datatype": "Tip podataka",
|
||||
"Date": "Datum",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "Odgodi do",
|
||||
"Delete": "Obriši",
|
||||
"DeleteShoppingConfirm": "Jesi li siguran da želiš ukloniti svu {food} s popisa za kupnju?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Obriši sve",
|
||||
"Delete_Food": "Obriši namirnicu",
|
||||
"Delete_Keyword": "Obriši ključnu riječ",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "Onemogući količinu",
|
||||
"Disabled": "Onemogućeno",
|
||||
"Documentation": "Dokumentacija",
|
||||
"DontChange": "",
|
||||
"Download": "Preuzimanje",
|
||||
"Drag_Here_To_Delete": "Povuci ovdje za brisanje",
|
||||
"Edit": "Uredi",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "Izvoz podržan",
|
||||
"Export_To_ICal": "Izvoz .ics",
|
||||
"External": "Vanjski",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Slika vanjskog recepta",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "FDC ID baze podataka",
|
||||
"FDC_Search": "FDC Pretraga",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Neuspješno",
|
||||
"Fats": "Masti",
|
||||
"File": "Datoteka",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "Sakrij ključnu riječ",
|
||||
"Hide_Recipes": "Sakrij Recepte",
|
||||
"Hide_as_header": "Sakrij kao zaglavlje",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Sat",
|
||||
"Hours": "Sati",
|
||||
"Icon": "Ikona",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "Logotip",
|
||||
"Make_Header": "Napravi zaglavlje",
|
||||
"Make_Ingredient": "Napravi sastojak",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Upravljaj knjigama",
|
||||
"Manage_Emails": "Upravljanje e-poštom",
|
||||
"Meal_Plan": "Plan obroka",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "Potreban je tip obroka",
|
||||
"Meal_Types": "Tipovi obroka",
|
||||
"Merge": "Spoji",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Spoji ključnu riječ",
|
||||
"Message": "Poruka",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "Nova jedinica",
|
||||
"Next_Day": "Sljedeći dan",
|
||||
"Next_Period": "Slijedeće razdoblje",
|
||||
"No": "",
|
||||
"NoCategory": "Nije odabrana kategorija.",
|
||||
"NoMoreUndo": "Nema promjena koje se mogu poništiti.",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "Prethodni dan",
|
||||
"Previous_Period": "Prethodno razdoblje",
|
||||
"Print": "Ispis",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Privatni Recept",
|
||||
"Private_Recipe_Help": "Recept se prikazuje samo vama i osobama s kojima se dijeli.",
|
||||
"Properties": "Svojstva",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "Recepti",
|
||||
"Recipes_In_Import": "Recepti u vašoj datoteci za uvoz",
|
||||
"Recipes_per_page": "Recepata po stranici",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Ukloni {food} sa svog popisa za kupovinu",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Izbrišite hranjive sastojke iz recepta",
|
||||
"Reset": "Ponovo postavi",
|
||||
"Reset_Search": "Poništi pretragu",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "Veličina",
|
||||
"Social_Authentication": "Autentifikacija putem društvenih mreža",
|
||||
"Sort_by_new": "Poredaj po novom",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Neke kozmetičke postavke mogu promijeniti administratori prostora i one će poništiti postavke klijenta za taj prostor.",
|
||||
"Split_All_Steps": "Podijeli sve retke u zasebne korake.",
|
||||
"StartDate": "Početni datum",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Vrsta",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Nepromijenjeno",
|
||||
"Undefined": "Nedefinirano",
|
||||
"Undo": "Poništi",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "Vrijedi do",
|
||||
"View": "Pogled",
|
||||
"View_Recipes": "Pogledajte recepte",
|
||||
"Visibility": "",
|
||||
"Waiting": "Čekanje",
|
||||
"Warning": "Upozorenje",
|
||||
"Warning_Delete_Supermarket_Category": "Brisanje kategorije supermarketa također će izbrisati sve odnose na namirnice. Jeste li sigurni?",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "Brojevi tjedana",
|
||||
"Welcome": "Dobrodošli",
|
||||
"Year": "Godina",
|
||||
"Yes": "",
|
||||
"add_keyword": "Dodaj ključnu riječ",
|
||||
"additional_options": "Dodatne mogućnosti",
|
||||
"advanced": "Napredno",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Fiók",
|
||||
"Add": "Hozzáadás",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "{food} hozzáadása bevásárlólistához",
|
||||
"AddToShopping": "Hozzáadás a bevásárlólistához",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "Automatizálás",
|
||||
"Automation": "Automatizálás",
|
||||
"Back": "Vissza",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Könyvjelző",
|
||||
"Books": "Könyvek",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "Kalóriák",
|
||||
"Cancel": "Mégsem",
|
||||
"Cannot_Add_Notes_To_Shopping": "A bevásárlólistához nem adható hozzá megjegyzés",
|
||||
@@ -65,6 +73,7 @@
|
||||
"Create_New_Unit": "Új mértékegység hozzáadása",
|
||||
"Current_Period": "Jelenlegi periódus",
|
||||
"Custom Filter": "Egyéni szűrő",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Bővítse Terét alapanyagok, mértékegységek és egyebek közösség által összeállított listájának importálásával, hogy ezzel is javítsa a receptgyűjteményét.",
|
||||
"Datatype": "Adattípus",
|
||||
"Date": "Dátum",
|
||||
@@ -75,6 +84,7 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "Törlés",
|
||||
"DeleteShoppingConfirm": "Biztos, hogy az összes {food}-t el akarja távolítani a bevásárlólistáról?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "Alapanyag törlése",
|
||||
"Delete_Keyword": "Kulcsszó törlése",
|
||||
"Description": "Megnevezés",
|
||||
@@ -83,6 +93,7 @@
|
||||
"Disable_Amount": "Összeg kikapcsolása",
|
||||
"Disabled": "Kikapcsolva",
|
||||
"Documentation": "Dokumentáció",
|
||||
"DontChange": "",
|
||||
"Download": "Letöltés",
|
||||
"Drag_Here_To_Delete": "Törléshez húzza ide",
|
||||
"Edit": "Szerkesztés",
|
||||
@@ -100,7 +111,9 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "Külső",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Külső receptkép",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Hiba",
|
||||
"Fats": "Zsírok",
|
||||
"File": "Fájl",
|
||||
@@ -123,6 +136,7 @@
|
||||
"Hide_Keywords": "Kulcsszó elrejtése",
|
||||
"Hide_Recipes": "Receptek elrejtése",
|
||||
"Hide_as_header": "Fejlécként elrejtve",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Óra",
|
||||
"Hours": "Óra",
|
||||
"Icon": "Ikon",
|
||||
@@ -169,6 +183,7 @@
|
||||
"Log_Recipe_Cooking": "Főzés naplózása",
|
||||
"Make_Header": "Átalakítás címsorra",
|
||||
"Make_Ingredient": "Összetevő létrehozása",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Könyvek kezelése",
|
||||
"Manage_Emails": "Levelezés kezelése",
|
||||
"Meal_Plan": "Menüterv",
|
||||
@@ -177,6 +192,7 @@
|
||||
"Meal_Type_Required": "Étkezés megadása kötelező",
|
||||
"Meal_Types": "Étkezések",
|
||||
"Merge": "Összefűzés",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Kulcsszó összevonása",
|
||||
"Message": "Üzenet",
|
||||
"MissingProperties": "",
|
||||
@@ -204,6 +220,7 @@
|
||||
"New_Unit": "Új mennyiségi egység",
|
||||
"Next_Day": "Következő nap",
|
||||
"Next_Period": "Következő periódus",
|
||||
"No": "",
|
||||
"NoCategory": "Nincs kategória kiválasztva.",
|
||||
"NoUnit": "",
|
||||
"No_ID": "Azonosító nem található, ezért nem törölhető.",
|
||||
@@ -242,6 +259,7 @@
|
||||
"Previous_Day": "Előző nap",
|
||||
"Previous_Period": "Előző periódus",
|
||||
"Print": "Nyomtatás",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Privát recept",
|
||||
"Private_Recipe_Help": "A recept csak Önnek és azoknak az embereknek jelenik meg, akikkel megosztotta.",
|
||||
"Properties": "Tulajdonságok",
|
||||
@@ -260,7 +278,9 @@
|
||||
"Recipes": "Receptek",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "Receptek oldalanként",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "{food} eltávolítása bevásárlólistáról",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Tápértékadatok törlése a receptből",
|
||||
"Reset": "Visszaállítás",
|
||||
"Reset_Search": "Keresés alaphelyzetbe állítása",
|
||||
@@ -293,6 +313,7 @@
|
||||
"Single": "Egyetlen",
|
||||
"Size": "Méret",
|
||||
"Sort_by_new": "Rendezés legújabbak szerint",
|
||||
"Space": "",
|
||||
"Split_All_Steps": "Ossza fel az összes sort különálló lépésekbe.",
|
||||
"StartDate": "Kezdés dátuma",
|
||||
"Starting_Day": "A hét kezdőnapja",
|
||||
@@ -320,6 +341,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Típus",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Meghatározatlan",
|
||||
"Unit": "Mennyiségi egység",
|
||||
"Unit_Alias": "",
|
||||
@@ -343,6 +365,7 @@
|
||||
"Valid Until": "Érvényes",
|
||||
"View": "Nézet",
|
||||
"View_Recipes": "Receptek megjelenítése",
|
||||
"Visibility": "",
|
||||
"Waiting": "Várakozás",
|
||||
"Warning": "Figyelmeztetés",
|
||||
"Warning_Delete_Supermarket_Category": "Egy szupermarket-kategória törlése az alapanyagokkal való összes kapcsolatot is törli. Biztos vagy benne?",
|
||||
@@ -351,6 +374,7 @@
|
||||
"Week_Numbers": "",
|
||||
"Welcome": "Üdvözöljük",
|
||||
"Year": "Év",
|
||||
"Yes": "",
|
||||
"add_keyword": "Kulcsszó hozzáadása",
|
||||
"additional_options": "További lehetőségek",
|
||||
"advanced": "Haladó",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Add": "",
|
||||
"AddChild": "",
|
||||
"Add_nutrition_recipe": "Ավելացնել սննդայնություն բաղադրատոմսին",
|
||||
"Add_to_Book": "",
|
||||
"Add_to_Plan": "Ավելացնել պլանին",
|
||||
@@ -7,7 +10,12 @@
|
||||
"Advanced Search Settings": "Ընդլայնված փնտրման կարգավորումներ",
|
||||
"Apply": "",
|
||||
"Automate": "Ավտոմատացնել",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Books": "",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "",
|
||||
"Cancel": "",
|
||||
"Carbohydrates": "",
|
||||
@@ -19,11 +27,14 @@
|
||||
"Create_New_Food": "Ավելացնել նոր սննդամթերք",
|
||||
"Create_New_Keyword": "Ավելացնել նոր բանալի բառ",
|
||||
"Create_New_Shopping Category": "Ստեղծել գնումների նոր կատեգորիա",
|
||||
"DELETE_ERROR": "",
|
||||
"Date": "",
|
||||
"Delete": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "Ջնջել սննդամթերքը",
|
||||
"Delete_Keyword": "Ջնջել բանալի բառը",
|
||||
"Description": "Նկարագրություն",
|
||||
"DontChange": "",
|
||||
"Download": "Ներբեռնել",
|
||||
"Edit": "",
|
||||
"Edit_Food": "Խմբագրել սննդամթերքը",
|
||||
@@ -33,7 +44,9 @@
|
||||
"Energy": "",
|
||||
"Export": "",
|
||||
"External": "",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "",
|
||||
"FETCH_ERROR": "",
|
||||
"Fats": "",
|
||||
"File": "",
|
||||
"Files": "",
|
||||
@@ -46,6 +59,7 @@
|
||||
"Hide_Keywords": "Թաքցնել բանալի բառը",
|
||||
"Hide_Recipes": "Թաքցնել բաղադրատոմսերը",
|
||||
"Hide_as_header": "Թաքցնել որպես խորագիր",
|
||||
"Hierarchy": "",
|
||||
"IgnoreAccents": "",
|
||||
"IgnoreAccentsHelp": "",
|
||||
"Import": "Ներմուծել",
|
||||
@@ -57,9 +71,11 @@
|
||||
"Load_More": "",
|
||||
"Log_Cooking": "Գրանցել եփելը",
|
||||
"Log_Recipe_Cooking": "Գրանցել բաղադրատոմսի օգտագործում",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Կարգավորել Գրքերը",
|
||||
"Meal_Plan": "Ճաշացուցակ",
|
||||
"Merge": "Միացնել",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Միացնել բանալի բառը",
|
||||
"MissingProperties": "",
|
||||
"Move": "Տեղափոխել",
|
||||
@@ -70,6 +86,7 @@
|
||||
"New_Food": "Նոր սննդամթերք",
|
||||
"New_Keyword": "Նոր բանալի բառ",
|
||||
"New_Recipe": "Նոր բաղադրատոմս",
|
||||
"No": "",
|
||||
"NoUnit": "",
|
||||
"No_Results": "Արդյունքներ չկան",
|
||||
"Nutrition": "",
|
||||
@@ -82,6 +99,8 @@
|
||||
"PrecisionSearchHelp": "",
|
||||
"Preparation": "",
|
||||
"Print": "Տպել",
|
||||
"Private": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Proteins": "",
|
||||
"Rating": "",
|
||||
"Recently_Viewed": "Վերջերս դիտած",
|
||||
@@ -90,6 +109,8 @@
|
||||
"Recipe_Image": "Բաղադրատոմսի նկար",
|
||||
"Recipes": "Բաղադրատոմսեր",
|
||||
"Recipes_per_page": "Բաղադրատոմս էջում",
|
||||
"RemoveAllType": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Հեռացնել բաղադրատոմսի սննդայնությունը",
|
||||
"Reset_Search": "Զրոյացնել որոնումը",
|
||||
"Save": "",
|
||||
@@ -109,6 +130,7 @@
|
||||
"Show_as_header": "Ցույց տալ որպես խորագիր",
|
||||
"Size": "",
|
||||
"Sort_by_new": "Տեսակավորել ըստ նորերի",
|
||||
"Space": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
"Step": "",
|
||||
@@ -118,6 +140,7 @@
|
||||
"Table_of_Contents": "Բովանդակություն",
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Url_Import": "URL ներմուծում",
|
||||
"Use_Plural_Food_Always": "",
|
||||
"Use_Plural_Food_Simple": "",
|
||||
@@ -125,7 +148,9 @@
|
||||
"Use_Plural_Unit_Simple": "",
|
||||
"View": "Դիտել",
|
||||
"View_Recipes": "Դիտել բաղադրատոմսերը",
|
||||
"Visibility": "",
|
||||
"Waiting": "",
|
||||
"Yes": "",
|
||||
"all_fields_optional": "Բոլոր տողերը կամավոր են և կարող են մնալ դատարկ։",
|
||||
"and": "և",
|
||||
"confirm_delete": "Համոզվա՞ծ եք, որ ուզում եք ջնջել այս {օբյեկտը}։",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "",
|
||||
"Add": "Tambahkan",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "",
|
||||
"AddToShopping": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -19,8 +22,13 @@
|
||||
"Auto_Planner": "",
|
||||
"Automate": "",
|
||||
"Automation": "Automatis",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "Buku",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "Kalori",
|
||||
"Cancel": "Batal",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
@@ -57,6 +65,7 @@
|
||||
"Create_New_Unit": "",
|
||||
"Current_Period": "",
|
||||
"Custom Filter": "",
|
||||
"DELETE_ERROR": "",
|
||||
"Date": "Tanggal",
|
||||
"Day": "",
|
||||
"Days": "",
|
||||
@@ -66,6 +75,7 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "Menghapus",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "",
|
||||
"Delete_Keyword": "Hapus Kata Kunci",
|
||||
"Description": "",
|
||||
@@ -73,6 +83,7 @@
|
||||
"Disable_Amount": "Nonaktifkan Jumlah",
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "Unduh",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Edit": "Sunting",
|
||||
@@ -89,7 +100,9 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "Luar",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Gambar Resep Eksternal",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Kegagalan",
|
||||
"Fats": "Lemak",
|
||||
"File": "Berkas",
|
||||
@@ -111,6 +124,7 @@
|
||||
"Hide_Keywords": "Sembunyikan Kata Kunci",
|
||||
"Hide_Recipes": "Sembunyikan Resep",
|
||||
"Hide_as_header": "Sembunyikan sebagai tajuk",
|
||||
"Hierarchy": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Icon": "",
|
||||
@@ -154,6 +168,7 @@
|
||||
"Log_Recipe_Cooking": "Log Resep Memasak",
|
||||
"Make_Header": "Buat Header",
|
||||
"Make_Ingredient": "Buat bahan",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Kelola Buku",
|
||||
"Manage_Emails": "",
|
||||
"Meal_Plan": "rencana makan",
|
||||
@@ -162,6 +177,7 @@
|
||||
"Meal_Type_Required": "",
|
||||
"Meal_Types": "",
|
||||
"Merge": "Menggabungkan",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Gabungkan Kata Kunci",
|
||||
"Message": "",
|
||||
"MissingProperties": "",
|
||||
@@ -188,6 +204,7 @@
|
||||
"New_Unit": "",
|
||||
"Next_Day": "",
|
||||
"Next_Period": "",
|
||||
"No": "",
|
||||
"NoCategory": "",
|
||||
"NoUnit": "",
|
||||
"No_ID": "",
|
||||
@@ -220,6 +237,7 @@
|
||||
"Previous_Day": "",
|
||||
"Previous_Period": "",
|
||||
"Print": "Mencetak",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Resep Pribadi",
|
||||
"Private_Recipe_Help": "Resep hanya diperlihatkan kepada Anda dan orang-orang yang dibagikan resep tersebut.",
|
||||
"Protected": "Terlindung",
|
||||
@@ -236,7 +254,9 @@
|
||||
"Recipes": "Resep",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "Resep per Halaman",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Hapus nutrisi dari resep",
|
||||
"Reset": "",
|
||||
"Reset_Search": "Setel Ulang Pencarian",
|
||||
@@ -270,6 +290,7 @@
|
||||
"Size": "Ukuran",
|
||||
"Social_Authentication": "",
|
||||
"Sort_by_new": "Urutkan berdasarkan baru",
|
||||
"Space": "",
|
||||
"Starting_Day": "",
|
||||
"StartsWith": "",
|
||||
"StartsWithHelp": "",
|
||||
@@ -296,6 +317,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "",
|
||||
"Unit": "",
|
||||
"Unit_Alias": "",
|
||||
@@ -311,6 +333,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "Melihat",
|
||||
"View_Recipes": "Lihat Resep",
|
||||
"Visibility": "",
|
||||
"Waiting": "Menunggu",
|
||||
"Warning": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -318,6 +341,7 @@
|
||||
"Week": "",
|
||||
"Week_Numbers": "",
|
||||
"Year": "",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "",
|
||||
"Add": "",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "",
|
||||
"AddToShopping": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "",
|
||||
"Automation": "",
|
||||
"Back": "",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "",
|
||||
"Calories": "",
|
||||
"Cancel": "",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "",
|
||||
"CustomTheme": "",
|
||||
"CustomThemeHelp": "",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "",
|
||||
"Datatype": "",
|
||||
"Date": "",
|
||||
@@ -85,6 +94,7 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "",
|
||||
"Delete_Food": "",
|
||||
"Delete_Keyword": "",
|
||||
@@ -94,6 +104,7 @@
|
||||
"Disable_Amount": "",
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Edit": "",
|
||||
@@ -113,10 +124,12 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "",
|
||||
"FDC_ID": "",
|
||||
"FDC_ID_help": "",
|
||||
"FDC_Search": "",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "",
|
||||
"Fats": "",
|
||||
"File": "",
|
||||
@@ -139,6 +152,7 @@
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Hide_as_header": "",
|
||||
"Hierarchy": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Icon": "",
|
||||
@@ -187,6 +201,7 @@
|
||||
"Logo": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "",
|
||||
"Manage_Emails": "",
|
||||
"Meal_Plan": "",
|
||||
@@ -195,6 +210,7 @@
|
||||
"Meal_Type_Required": "",
|
||||
"Meal_Types": "",
|
||||
"Merge": "",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "",
|
||||
"Message": "",
|
||||
"MissingProperties": "",
|
||||
@@ -225,6 +241,7 @@
|
||||
"New_Unit": "",
|
||||
"Next_Day": "",
|
||||
"Next_Period": "",
|
||||
"No": "",
|
||||
"NoCategory": "",
|
||||
"NoMoreUndo": "",
|
||||
"NoUnit": "",
|
||||
@@ -265,6 +282,7 @@
|
||||
"Previous_Day": "",
|
||||
"Previous_Period": "",
|
||||
"Print": "",
|
||||
"Private": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Properties": "",
|
||||
@@ -286,7 +304,9 @@
|
||||
"Recipes": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Reset": "",
|
||||
"Reset_Search": "",
|
||||
@@ -324,6 +344,7 @@
|
||||
"Size": "",
|
||||
"Social_Authentication": "",
|
||||
"Sort_by_new": "",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "",
|
||||
"Split_All_Steps": "",
|
||||
"StartDate": "",
|
||||
@@ -354,6 +375,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "",
|
||||
"Undefined": "",
|
||||
"Undo": "",
|
||||
@@ -381,6 +403,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "",
|
||||
"View_Recipes": "",
|
||||
"Visibility": "",
|
||||
"Waiting": "",
|
||||
"Warning": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -389,6 +412,7 @@
|
||||
"Week_Numbers": "",
|
||||
"Welcome": "",
|
||||
"Year": "",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "",
|
||||
"Add": "",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "",
|
||||
"AddToShopping": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "",
|
||||
"Automation": "",
|
||||
"Back": "",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "",
|
||||
"Cancel": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
@@ -66,6 +74,7 @@
|
||||
"Create_New_Unit": "",
|
||||
"Current_Period": "",
|
||||
"Custom Filter": "",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "",
|
||||
"Datatype": "",
|
||||
"Date": "",
|
||||
@@ -77,6 +86,7 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "",
|
||||
"Delete_Keyword": "Ištrinti raktažodį",
|
||||
"Description": "",
|
||||
@@ -85,6 +95,7 @@
|
||||
"Disable_Amount": "Išjungti sumą",
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Edit": "",
|
||||
@@ -102,7 +113,9 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Išorinis recepto vaizdas",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "",
|
||||
"Fats": "",
|
||||
"File": "",
|
||||
@@ -125,6 +138,7 @@
|
||||
"Hide_Keywords": "Paslėpti raktažodį",
|
||||
"Hide_Recipes": "Paslėpti receptus",
|
||||
"Hide_as_header": "Slėpti kaip antraštę",
|
||||
"Hierarchy": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Icon": "",
|
||||
@@ -171,6 +185,7 @@
|
||||
"Log_Recipe_Cooking": "Užregistruoti recepto pagaminimą",
|
||||
"Make_Header": "Padaryti antraštę",
|
||||
"Make_Ingredient": "Padaryti ingredientą",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Tvarkyti knygas",
|
||||
"Manage_Emails": "",
|
||||
"Meal_Plan": "Maisto planas",
|
||||
@@ -179,6 +194,7 @@
|
||||
"Meal_Type_Required": "",
|
||||
"Meal_Types": "",
|
||||
"Merge": "",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Sujungti raktažodį",
|
||||
"Message": "",
|
||||
"MissingProperties": "",
|
||||
@@ -207,6 +223,7 @@
|
||||
"New_Unit": "",
|
||||
"Next_Day": "",
|
||||
"Next_Period": "",
|
||||
"No": "",
|
||||
"NoCategory": "",
|
||||
"NoUnit": "",
|
||||
"No_ID": "",
|
||||
@@ -246,6 +263,7 @@
|
||||
"Previous_Day": "",
|
||||
"Previous_Period": "",
|
||||
"Print": "",
|
||||
"Private": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Properties": "",
|
||||
@@ -264,7 +282,9 @@
|
||||
"Recipes": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "Receptų skaičius per puslapį",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Ištrinti mitybos informaciją iš recepto",
|
||||
"Reset": "",
|
||||
"Reset_Search": "Iš naujo nustatyti paiešką",
|
||||
@@ -298,6 +318,7 @@
|
||||
"Size": "",
|
||||
"Social_Authentication": "",
|
||||
"Sort_by_new": "Rūšiuoti pagal naujumą",
|
||||
"Space": "",
|
||||
"Split_All_Steps": "",
|
||||
"StartDate": "",
|
||||
"Starting_Day": "",
|
||||
@@ -327,6 +348,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "",
|
||||
"Unit": "",
|
||||
"Unit_Alias": "",
|
||||
@@ -351,6 +373,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "",
|
||||
"View_Recipes": "Žiūrėti receptus",
|
||||
"Visibility": "",
|
||||
"Waiting": "",
|
||||
"Warning": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -359,6 +382,7 @@
|
||||
"Week_Numbers": "",
|
||||
"Welcome": "",
|
||||
"Year": "",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "",
|
||||
"Add": "",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "",
|
||||
"AddToShopping": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
@@ -24,8 +27,13 @@
|
||||
"Automate": "",
|
||||
"Automation": "",
|
||||
"Back": "",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "",
|
||||
"Calories": "",
|
||||
"Cancel": "",
|
||||
@@ -74,6 +82,7 @@
|
||||
"CustomNavLogoHelp": "",
|
||||
"CustomTheme": "",
|
||||
"CustomThemeHelp": "",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "",
|
||||
"Datatype": "",
|
||||
"Date": "",
|
||||
@@ -86,6 +95,7 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "",
|
||||
"Delete_Food": "",
|
||||
"Delete_Keyword": "",
|
||||
@@ -95,6 +105,7 @@
|
||||
"Disable_Amount": "",
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Edit": "",
|
||||
@@ -114,10 +125,12 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "",
|
||||
"External": "",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "",
|
||||
"FDC_ID": "",
|
||||
"FDC_ID_help": "",
|
||||
"FDC_Search": "",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "",
|
||||
"Fats": "",
|
||||
"File": "",
|
||||
@@ -140,6 +153,7 @@
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Hide_as_header": "",
|
||||
"Hierarchy": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Icon": "",
|
||||
@@ -188,6 +202,7 @@
|
||||
"Logo": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "",
|
||||
"Manage_Emails": "",
|
||||
"Meal_Plan": "",
|
||||
@@ -196,6 +211,7 @@
|
||||
"Meal_Type_Required": "",
|
||||
"Meal_Types": "",
|
||||
"Merge": "",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "",
|
||||
"Message": "",
|
||||
"MissingProperties": "",
|
||||
@@ -226,6 +242,7 @@
|
||||
"New_Unit": "",
|
||||
"Next_Day": "",
|
||||
"Next_Period": "",
|
||||
"No": "",
|
||||
"NoCategory": "",
|
||||
"NoMoreUndo": "",
|
||||
"NoUnit": "",
|
||||
@@ -266,6 +283,7 @@
|
||||
"Previous_Day": "",
|
||||
"Previous_Period": "",
|
||||
"Print": "",
|
||||
"Private": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"Properties": "",
|
||||
@@ -287,7 +305,9 @@
|
||||
"Recipes": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Reset": "",
|
||||
"Reset_Search": "",
|
||||
@@ -326,6 +346,7 @@
|
||||
"Size": "",
|
||||
"Social_Authentication": "",
|
||||
"Sort_by_new": "",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "",
|
||||
"Split_All_Steps": "",
|
||||
"StartDate": "",
|
||||
@@ -356,6 +377,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "",
|
||||
"Undefined": "",
|
||||
"Undo": "",
|
||||
@@ -383,6 +405,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "",
|
||||
"View_Recipes": "",
|
||||
"Visibility": "",
|
||||
"Waiting": "",
|
||||
"Warning": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -391,6 +414,7 @@
|
||||
"Week_Numbers": "",
|
||||
"Welcome": "",
|
||||
"Year": "",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "",
|
||||
"Add": "Legg til",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Legg til {food] i handlelisten din",
|
||||
"AddToShopping": "Legg til i handleliste",
|
||||
"Add_Servings_to_Shopping": "Legg til {servings} serveringer i handlelisten",
|
||||
@@ -23,8 +26,13 @@
|
||||
"Auto_Sort_Help": "Flytt alle ingredienser til det mest passende steget.",
|
||||
"Automate": "Automatiser",
|
||||
"Automation": "Automatiser",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "",
|
||||
"Books": "Bøker",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Kalkulator",
|
||||
"Calories": "Kalorier",
|
||||
"Cancel": "Avbryt",
|
||||
@@ -71,6 +79,7 @@
|
||||
"CustomNavLogoHelp": "Last opp logo til navigasjonsområde.",
|
||||
"CustomTheme": "Egendefinert tema",
|
||||
"CustomThemeHelp": "Overskriv det valgte tema ved å laste opp en egendefinert CSS-fil.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Utvid ditt \"Space\" ved å importere en felleskapsberiket liste over mat, enheter og mer for å berike din oppskriftskolleksjon.",
|
||||
"Datatype": "Data-type",
|
||||
"Date": "Dato",
|
||||
@@ -82,6 +91,7 @@
|
||||
"DelayUntil": "Forsink til",
|
||||
"Delete": "Slett",
|
||||
"DeleteShoppingConfirm": "Er du sikker på at du fjerne alle {food} fra handlelisten?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Slett alle",
|
||||
"Delete_Food": "Slett Matrett",
|
||||
"Delete_Keyword": "Slett nøkkelord",
|
||||
@@ -91,6 +101,7 @@
|
||||
"Disable_Amount": "Deaktiver mengde",
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"DontChange": "",
|
||||
"Download": "Last ned",
|
||||
"Drag_Here_To_Delete": "Dra her for å slette",
|
||||
"Edit": "Rediger",
|
||||
@@ -108,8 +119,10 @@
|
||||
"Export_Supported": "",
|
||||
"Export_To_ICal": "Eksporter .ics",
|
||||
"External": "Ekstern",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Bilde av ekstern oppskrift",
|
||||
"FDC_ID_help": "FDC database-ID",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Feil",
|
||||
"Fats": "Fett",
|
||||
"File": "Fil",
|
||||
@@ -131,6 +144,7 @@
|
||||
"Hide_Keywords": "Skjul nøkkelord",
|
||||
"Hide_Recipes": "Skjul oppskrifter",
|
||||
"Hide_as_header": "Skjul overskrift",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Time",
|
||||
"Hours": "Timer",
|
||||
"Icon": "Ikon",
|
||||
@@ -178,6 +192,7 @@
|
||||
"Log_Recipe_Cooking": "Logg oppskriftsbruk",
|
||||
"Make_Header": "Bruk som overskrift",
|
||||
"Make_Ingredient": "Bruk som ingrediens",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Administrer bøker",
|
||||
"Manage_Emails": "Administrer e-poster",
|
||||
"Meal_Plan": "Måltidsplan",
|
||||
@@ -186,6 +201,7 @@
|
||||
"Meal_Type_Required": "Måltidstype er nødvendig",
|
||||
"Meal_Types": "Måltidstyper",
|
||||
"Merge": "Slå sammen",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Slå sammen nøkkelord",
|
||||
"Message": "Melding",
|
||||
"MissingProperties": "",
|
||||
@@ -212,6 +228,7 @@
|
||||
"New_Unit": "Ny Enhet",
|
||||
"Next_Day": "Neste dag",
|
||||
"Next_Period": "Neste periode",
|
||||
"No": "",
|
||||
"NoCategory": "Ingen kategori valgt.",
|
||||
"NoMoreUndo": "Ingen endringer å angre.",
|
||||
"NoUnit": "",
|
||||
@@ -251,6 +268,7 @@
|
||||
"Previous_Day": "Forrige dag",
|
||||
"Previous_Period": "Forrige periode",
|
||||
"Print": "Skriv ut",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Privat Oppskrift",
|
||||
"Private_Recipe_Help": "Oppskriften er bare vist til deg og dem du har delt den med.",
|
||||
"Properties": "Egenskaper",
|
||||
@@ -271,7 +289,9 @@
|
||||
"Recipes": "Oppskrift",
|
||||
"Recipes_In_Import": "",
|
||||
"Recipes_per_page": "Oppskrifter per side",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Fjern {food} fra handelisten din",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Fjern næringsinnhold fra oppskrift",
|
||||
"Reset": "",
|
||||
"Reset_Search": "Nullstill søk",
|
||||
@@ -308,6 +328,7 @@
|
||||
"Size": "Størrelse",
|
||||
"Social_Authentication": "",
|
||||
"Sort_by_new": "Sorter etter nyest",
|
||||
"Space": "",
|
||||
"Split_All_Steps": "",
|
||||
"StartDate": "Startdato",
|
||||
"Starting_Day": "Dag uken skal state på",
|
||||
@@ -336,6 +357,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Type",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Udefinert",
|
||||
"Undo": "Angre",
|
||||
"Unit": "Enhet",
|
||||
@@ -360,6 +382,7 @@
|
||||
"Valid Until": "",
|
||||
"View": "Visning",
|
||||
"View_Recipes": "Vis oppskrifter",
|
||||
"Visibility": "",
|
||||
"Waiting": "Venter",
|
||||
"Warning": "Advarsel",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
@@ -368,6 +391,7 @@
|
||||
"Week_Numbers": "Ukenummer",
|
||||
"Welcome": "Velkommen",
|
||||
"Year": "År",
|
||||
"Yes": "",
|
||||
"add_keyword": "",
|
||||
"additional_options": "",
|
||||
"advanced": "Avansert",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,18 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"AccessTokenHelp": "Klucze dostępu do REST API.",
|
||||
"Access_Token": "Token Dostępu",
|
||||
"Account": "Konto",
|
||||
"Actions": "Akcje",
|
||||
"Activity": "Aktywność",
|
||||
"Add": "Dodaj",
|
||||
"AddAll": "Dodaj wszystkie",
|
||||
"AddChild": "",
|
||||
"AddFilter": "Dodaj filtr",
|
||||
"AddFoodToShopping": "Dodaj {food} do swojej listy zakupów",
|
||||
"AddMany": "Dodaj wiele",
|
||||
"AddToShopping": "Dodaj do listy zakupów",
|
||||
"Add_Servings_to_Shopping": "Dodaj {servings} porcje do zakupów",
|
||||
"Add_Step": "Dodaj krok",
|
||||
@@ -12,26 +22,46 @@
|
||||
"Added_To_Shopping_List": "Dodano do listy zakupów",
|
||||
"Added_by": "Dodane przez",
|
||||
"Added_on": "Dodano dnia",
|
||||
"Admin": "Administator",
|
||||
"Advanced": "Zaawansowany",
|
||||
"Advanced Search Settings": "Ustawienia zaawansowanego wyszukiwania",
|
||||
"Alignment": "Wyrównanie",
|
||||
"AllRecipes": "Wszystkie przepisy",
|
||||
"Amount": "Ilość",
|
||||
"App": "Aplikacja",
|
||||
"Apply": "",
|
||||
"AppImportSubtitle": "Zaimportuj istniejącą bazę przepisów.",
|
||||
"Apply": "Zastosuj",
|
||||
"Are_You_Sure": "Jesteś pewny?",
|
||||
"Auto_Planner": "Plan automatyczny",
|
||||
"Auto_Sort": "Auto sortowanie",
|
||||
"Auto_Sort_Help": "Przenieś wszystkie składniki do najlepiej dopasowanego kroku.",
|
||||
"Automate": "Automatyzacja",
|
||||
"Automation": "Automatyzacja",
|
||||
"AutomationHelp": "Automatyzacje pozwalają Ci, zależnie od typu, zastosować niektóre automatyczne zmiany w przepisach, składnikach, ... na przykład podczas importowania przepisów. ",
|
||||
"Available": "Dostępne",
|
||||
"AvailableCategories": "Dostępne kategorie",
|
||||
"Back": "Z powrotem",
|
||||
"BaseUnit": "Podstawowa jednostka",
|
||||
"BaseUnitHelp": "Standardowa jednostka dla automatyczne konwersji jednostek",
|
||||
"Basics": "Podstawy",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Book": "Książka",
|
||||
"Bookmarklet": "Skryptozakładka",
|
||||
"BookmarkletHelp1": "Przeciągnij następujący przycisk do twojego paska zakładek",
|
||||
"BookmarkletHelp2": "Otwórz stonę z której chcesz zaimportować",
|
||||
"BookmarkletHelp3": "Kliknij na zakładkę, aby wykonać import.",
|
||||
"BookmarkletImportSubtitle": "Użyj skryptozakładki do zaimportowania z niepublicznych stron.",
|
||||
"Books": "Książki",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Kalkulator",
|
||||
"Calories": "Kalorie",
|
||||
"Cancel": "Anuluj",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notatki nie mogą być dodawane do listy zakupów",
|
||||
"Carbohydrates": "Węglowodany",
|
||||
"Cards": "Karty",
|
||||
"Categories": "Kategorie",
|
||||
"Category": "Kategorie",
|
||||
"CategoryInstruction": "Przeciągnij kategorie, aby zmienić kolejność w jakiej kategorie pojawiają się na liście zakupów.",
|
||||
@@ -47,8 +77,11 @@
|
||||
"Color": "Kolor",
|
||||
"Combine_All_Steps": "Połącz wszystkie kroki w jedno pole.",
|
||||
"Coming_Soon": "Dostępne wkrótce",
|
||||
"Comment": "Komentarz",
|
||||
"Comments_setting": "Pokaż komentarze",
|
||||
"Completed": "Zakończone",
|
||||
"Confirm": "Potwierdź",
|
||||
"Continue": "Kontynuuj",
|
||||
"Conversion": "Konwersja",
|
||||
"Copy": "Kopiuj",
|
||||
"Copy Link": "Skopiuj link",
|
||||
@@ -75,6 +108,7 @@
|
||||
"CustomNavLogoHelp": "Prześlij obraz, który będzie używany jako logo paska nawigacyjnego.",
|
||||
"CustomTheme": "Własny motyw",
|
||||
"CustomThemeHelp": "Zastąp style wybranego motywu, przesyłając własny plik CSS.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Wzbogać swoją Przestrzeń, importując wyselekcjonowaną przez społeczność listę żywności, jednostek i nie tylko, aby ulepszyć swoją kolekcję przepisów.",
|
||||
"Datatype": "Typ danych",
|
||||
"Date": "Data",
|
||||
@@ -87,6 +121,7 @@
|
||||
"DelayUntil": "Opóźnij do",
|
||||
"Delete": "Usuń",
|
||||
"DeleteShoppingConfirm": "Czy na pewno chcesz usunąć wszystkie {food} z listy zakupów?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Usuń wszystko",
|
||||
"Delete_Food": "Usuń żywność",
|
||||
"Delete_Keyword": "Usuń słowo kluczowe",
|
||||
@@ -96,6 +131,7 @@
|
||||
"Disable_Amount": "Wyłącz ilość",
|
||||
"Disabled": "Wyłączone",
|
||||
"Documentation": "Dokumentacja",
|
||||
"DontChange": "",
|
||||
"Download": "Pobieranie",
|
||||
"Drag_Here_To_Delete": "Przeciągnij tutaj, aby usunąć",
|
||||
"Edit": "Edytuj",
|
||||
@@ -115,10 +151,12 @@
|
||||
"Export_Supported": "Eksportowanie wspierane",
|
||||
"Export_To_ICal": "Eksportuj .ics",
|
||||
"External": "Zewnętrzny",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Zewnętrzny obraz dla przepisu",
|
||||
"FDC_ID": "Identyfikator FDC",
|
||||
"FDC_ID_help": "Identyfikator bazy FDC",
|
||||
"FDC_Search": "Wyszukiwanie w FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Niepowodzenie",
|
||||
"Fats": "Tłuszcze",
|
||||
"File": "Plik",
|
||||
@@ -141,6 +179,7 @@
|
||||
"Hide_Keywords": "Ukryj słowo kluczowe",
|
||||
"Hide_Recipes": "Ukryj przepisy",
|
||||
"Hide_as_header": "Ukryj jako nagłówek",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Godzina",
|
||||
"Hours": "Godziny",
|
||||
"Icon": "Ikona",
|
||||
@@ -189,6 +228,7 @@
|
||||
"Logo": "Logo",
|
||||
"Make_Header": "Utwórz nagłówek",
|
||||
"Make_Ingredient": "Utwórz składnik",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Zarządzaj książkami",
|
||||
"Manage_Emails": "Zarządzaj e-mailami",
|
||||
"Meal_Plan": "Plan posiłków",
|
||||
@@ -197,6 +237,7 @@
|
||||
"Meal_Type_Required": "Rodzaj posiłku jest wymagany",
|
||||
"Meal_Types": "Rodzaje posiłków",
|
||||
"Merge": "Scal",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Scal słowa kluczowe",
|
||||
"Message": "Wiadomość",
|
||||
"MissingProperties": "",
|
||||
@@ -227,6 +268,7 @@
|
||||
"New_Unit": "Nowa jednostka",
|
||||
"Next_Day": "Następny dzień",
|
||||
"Next_Period": "Następny okres",
|
||||
"No": "",
|
||||
"NoCategory": "Nie wybrano kategorii.",
|
||||
"NoMoreUndo": "Brak zmian do wycofania.",
|
||||
"NoUnit": "",
|
||||
@@ -267,6 +309,7 @@
|
||||
"Previous_Day": "Poprzedni dzień",
|
||||
"Previous_Period": "Poprzedni okres",
|
||||
"Print": "Drukuj",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Prywatny przepis",
|
||||
"Private_Recipe_Help": "Przepis jest widoczny tylko dla Ciebie i dla osób, którym jest udostępniany.",
|
||||
"Properties": "Właściwości",
|
||||
@@ -288,7 +331,9 @@
|
||||
"Recipes": "Przepisy",
|
||||
"Recipes_In_Import": "Przepisy w pliku importu",
|
||||
"Recipes_per_page": "Przepisy na stronę",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Usuń {food} z listy zakupów",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Usuń wartości odżywcze z przepisu",
|
||||
"Reset": "Resetowanie",
|
||||
"Reset_Search": "Resetuj wyszukiwanie",
|
||||
@@ -327,6 +372,7 @@
|
||||
"Size": "Rozmiar",
|
||||
"Social_Authentication": "Uwierzytelnianie społecznościowe",
|
||||
"Sort_by_new": "Sortuj według nowych",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Administratorzy przestrzeni mogą zmienić niektóre ustawienia kosmetyczne, które zastąpią ustawienia klienta dla tej przestrzeni.",
|
||||
"Split_All_Steps": "Traktuj każdy wiersz jako osobne kroki.",
|
||||
"StartDate": "Data początkowa",
|
||||
@@ -357,6 +403,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Typ",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Niezmienione",
|
||||
"Undefined": "Nieokreślony",
|
||||
"Undo": "Cofnij",
|
||||
@@ -384,6 +431,7 @@
|
||||
"Valid Until": "Ważne do",
|
||||
"View": "Pogląd",
|
||||
"View_Recipes": "Przeglądaj przepisy",
|
||||
"Visibility": "",
|
||||
"Waiting": "Oczekiwanie",
|
||||
"Warning": "Ostrzeżenie",
|
||||
"Warning_Delete_Supermarket_Category": "Usunięcie kategorii supermarketu spowoduje również usunięcie wszystkich relacji z żywnością. Jesteś pewny?",
|
||||
@@ -392,6 +440,7 @@
|
||||
"Week_Numbers": "Numery tygodni",
|
||||
"Welcome": "Witamy",
|
||||
"Year": "Rok",
|
||||
"Yes": "",
|
||||
"add_keyword": "Dodaj słowo kluczowe",
|
||||
"additional_options": "Opcje dodatkowe",
|
||||
"advanced": "Zaawansowany",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Add": "Adicionar",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Adicionar {food} à sua lista de compras",
|
||||
"AddToShopping": "Adicionar á lista de compras",
|
||||
"Add_Servings_to_Shopping": "Adicionar {servings} doses ás compras",
|
||||
@@ -19,7 +22,12 @@
|
||||
"Auto_Sort_Help": "Mover todos os ingredientes para o passo mais indicado.",
|
||||
"Automate": "Automatizar",
|
||||
"Automation": "Automação",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Books": "Livros",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Calculadora",
|
||||
"Calories": "Calorias",
|
||||
"Cancel": "Cancelar",
|
||||
@@ -57,6 +65,7 @@
|
||||
"CustomImageHelp": "Fazer upload de uma image para mostrar na visão geral do espaço.",
|
||||
"CustomTheme": "Tema customizado",
|
||||
"CustomThemeHelp": "Substituir os estilos do tema selecionado com o upload de um arquivo CSS customizado.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Melhore seu Espaço importando uma lista curada de comidas, unidades e mais para melhorar sua coleção de receitas.",
|
||||
"Datatype": "Tipo de dados",
|
||||
"Date": "Data",
|
||||
@@ -66,12 +75,14 @@
|
||||
"DelayUntil": "",
|
||||
"Delete": "Apagar",
|
||||
"DeleteShoppingConfirm": "Tem a certeza que pretende remover toda {food} da sua lista de compras?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Apagar todos",
|
||||
"Delete_Food": "Eliminar comida",
|
||||
"Delete_Keyword": "Eliminar Palavra Chave",
|
||||
"Description": "Descrição",
|
||||
"Description_Replace": "Substituir descrição",
|
||||
"Disable_Amount": "Desativar quantidade",
|
||||
"DontChange": "",
|
||||
"Download": "Transferência",
|
||||
"Drag_Here_To_Delete": "Arraste para aqui para eliminar",
|
||||
"Edit": "Editar",
|
||||
@@ -87,10 +98,12 @@
|
||||
"Export_As_ICal": "Exportar período atual para o formato ICal",
|
||||
"Export_To_ICal": "Exportar .ics",
|
||||
"External": "Externo",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Imagem da receita externa",
|
||||
"FDC_ID": "ID FDC",
|
||||
"FDC_ID_help": "ID database FDC",
|
||||
"FDC_Search": "Pesquisa FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Falha",
|
||||
"Fats": "Gorduras",
|
||||
"File": "Ficheiro",
|
||||
@@ -111,6 +124,7 @@
|
||||
"Hide_Keywords": "Esconder palavra-chave",
|
||||
"Hide_Recipes": "Esconder Receitas",
|
||||
"Hide_as_header": "Esconder como cabeçalho",
|
||||
"Hierarchy": "",
|
||||
"Icon": "Ícone",
|
||||
"IgnoreAccents": "",
|
||||
"IgnoreAccentsHelp": "",
|
||||
@@ -145,6 +159,7 @@
|
||||
"Log_Recipe_Cooking": "Registrar Receitas de Culinária",
|
||||
"Make_Header": "Tornar cabeçalho",
|
||||
"Make_Ingredient": "Fazer ingrediente",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Gerenciar Livros",
|
||||
"Meal_Plan": "Plano de Refeição",
|
||||
"Meal_Plan_Days": "Planos de alimentação futuros",
|
||||
@@ -152,6 +167,7 @@
|
||||
"Meal_Type_Required": "Tipo de refeição é necessário",
|
||||
"Meal_Types": "Tipos de refeições",
|
||||
"Merge": "Juntar",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Unir palavra-chave",
|
||||
"MissingProperties": "",
|
||||
"Month": "Mês",
|
||||
@@ -172,6 +188,7 @@
|
||||
"New_Unit": "Nova Unidade",
|
||||
"Next_Day": "Dia seguinte",
|
||||
"Next_Period": "Próximo período",
|
||||
"No": "",
|
||||
"NoCategory": "Nenhuma categoria selecionada.",
|
||||
"NoMoreUndo": "Nenhuma alteração para ser desfeita.",
|
||||
"NoUnit": "",
|
||||
@@ -209,6 +226,7 @@
|
||||
"Previous_Day": "Dia anterior",
|
||||
"Previous_Period": "Período anterior",
|
||||
"Print": "Imprimir",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Receita Privada",
|
||||
"Private_Recipe_Help": "A receita só é mostrada ás pessoas com que foi partilhada.",
|
||||
"Properties": "Propriedades",
|
||||
@@ -229,7 +247,9 @@
|
||||
"Recipe_Image": "Imagem da Receita",
|
||||
"Recipes": "Receitas",
|
||||
"Recipes_per_page": "Receitas por página",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Remover valor nutricional da receita",
|
||||
"Reset": "Reiniciar",
|
||||
"Reset_Search": "Repor Pesquisa",
|
||||
@@ -259,6 +279,7 @@
|
||||
"Show_as_header": "Mostrar como cabeçalho",
|
||||
"Size": "Tamanho",
|
||||
"Sort_by_new": "Ordenar por mais recente",
|
||||
"Space": "",
|
||||
"StartDate": "Data de início",
|
||||
"Starting_Day": "Dia de início da semana",
|
||||
"StartsWith": "",
|
||||
@@ -283,6 +304,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Tipo",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Não definido",
|
||||
"Undo": "Desfazer",
|
||||
"Unit": "Unidade",
|
||||
@@ -301,12 +323,14 @@
|
||||
"User": "Utilizador",
|
||||
"View": "Vista",
|
||||
"View_Recipes": "Ver Receitas",
|
||||
"Visibility": "",
|
||||
"Waiting": "Em espera",
|
||||
"Warning": "Aviso",
|
||||
"Week": "Semana",
|
||||
"Week_Numbers": "Números das semanas",
|
||||
"Welcome": "Bem-vindo",
|
||||
"Year": "Ano",
|
||||
"Yes": "",
|
||||
"add_keyword": "Adicionar Palavra Chave",
|
||||
"advanced": "",
|
||||
"advanced_search_settings": "Configurações Avançadas de Pesquisa",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"AI": "IA",
|
||||
"AIImportSubtitle": "Use IA para importar imagens das receitas.",
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"AccessTokenHelp": "Chaves de acesso para a REST API.",
|
||||
"Access_Token": "Token de acesso",
|
||||
"Account": "Conta",
|
||||
@@ -9,6 +11,7 @@
|
||||
"Activity": "Atividade",
|
||||
"Add": "Adicionar",
|
||||
"AddAll": "Adicionar todos",
|
||||
"AddChild": "",
|
||||
"AddFilter": "Adicionar Filtro",
|
||||
"AddFoodToShopping": "Incluir {food} na sua lista de compras",
|
||||
"AddMany": "Adicionar muitos",
|
||||
@@ -28,6 +31,7 @@
|
||||
"Amount": "Quantidade",
|
||||
"App": "Aplicação",
|
||||
"AppImportSubtitle": "Importar seu banco de dados de receitas existente.",
|
||||
"Apply": "Aplicar",
|
||||
"Are_You_Sure": "Você tem certeza?",
|
||||
"Auto_Planner": "Planejamento Automático",
|
||||
"Auto_Sort": "Classificação automática",
|
||||
@@ -41,6 +45,10 @@
|
||||
"BaseUnit": "Unidade Base",
|
||||
"BaseUnitHelp": "Unidade padrão para conversão de unidades",
|
||||
"Basics": "Básicos",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Book": "Livro",
|
||||
"Bookmarklet": "Marcador",
|
||||
"BookmarkletHelp1": "Arraste o seguinte botão para sua barra de favoritos",
|
||||
@@ -48,6 +56,7 @@
|
||||
"BookmarkletHelp3": "Clique no favorito para realizar a importação.",
|
||||
"BookmarkletImportSubtitle": "Use um bookmarklet para importar de páginas não públicas.",
|
||||
"Books": "Livros",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Calculadora",
|
||||
"Calories": "Calorias",
|
||||
"Cancel": "Cancelar",
|
||||
@@ -69,6 +78,7 @@
|
||||
"Color": "Cor",
|
||||
"Combine_All_Steps": "Combinar todos os passos em um único campo.",
|
||||
"Coming_Soon": "Em breve",
|
||||
"Comment": "Comentário",
|
||||
"Comments_setting": "Mostrar Comentários",
|
||||
"Completed": "Finalizado",
|
||||
"Confirm": "Confirmar",
|
||||
@@ -108,6 +118,7 @@
|
||||
"CustomNavLogoHelp": "Faça upload de uma imagem para usar como logotipo na barra de navegação.",
|
||||
"CustomTheme": "Tema Personalizado",
|
||||
"CustomThemeHelp": "Substituir estilos do tema selecionado fazendo upload de um arquivo CSS personalizado.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Enriqueça seu espaço importando uma lista comunitariamente curada de alimentos, unidades e mais para melhorar sua coleção de receitas.",
|
||||
"Database": "Banco de dados",
|
||||
"DatabaseHelp": "O Tandoor utiliza diversos recursos para que você possa criar receitas, listas de compras, planos de refeições e muito mais. Aqui você pode gerenciar todos esses modelos.",
|
||||
@@ -124,66 +135,104 @@
|
||||
"Delete": "Deletar",
|
||||
"DeleteConfirmQuestion": "Tem certeza que quer excluir esse objeto?",
|
||||
"DeleteShoppingConfirm": "Tem certeza que deseja remover todas {food} de sua lista de compras?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Excluir tudo",
|
||||
"Delete_Food": "Deletar Comida",
|
||||
"Delete_Keyword": "Deletar palavra-chave",
|
||||
"Deleted": "Excluído",
|
||||
"Description": "Descrição",
|
||||
"Description_Replace": "Substituir Descrição",
|
||||
"DeviceSettings": "Configurações do Dispositivo",
|
||||
"DeviceSettingsHelp": "Para que o Tandoor fique bonito onde quer que você use, essas configurações são armazenadas neste dispositivo.",
|
||||
"Disable": "Desabilitar",
|
||||
"Disable_Amount": "Desabilitar Quantidade",
|
||||
"Disabled": "Desabilitado",
|
||||
"Documentation": "Documentação",
|
||||
"DontChange": "",
|
||||
"Down": "Abaixo",
|
||||
"Download": "Baixar",
|
||||
"DragToUpload": "Clique e arraste ou clique para selecionar",
|
||||
"Drag_Here_To_Delete": "Arraste aqui para deletar",
|
||||
"Duplicate": "Duplicar",
|
||||
"DuplicateFoundInfo": "Uma receita com essa URL foi encontrada no seu espaço. Continuar mesmo assim?",
|
||||
"Edit": "Editar",
|
||||
"Edit_Food": "Editar Comida",
|
||||
"Edit_Keyword": "Editar palavra-chave",
|
||||
"Edit_Meal_Plan_Entry": "Editar plano de refeição",
|
||||
"Edit_Recipe": "Editar Receita",
|
||||
"Email": "E-mail",
|
||||
"Empty": "Vazio",
|
||||
"Enable": "Ativar",
|
||||
"Enable_Amount": "Habilitar Quantidade",
|
||||
"Enabled": "Ativo",
|
||||
"EndDate": "Data Fim",
|
||||
"Energy": "Energia",
|
||||
"Entries": "Registros",
|
||||
"Error": "Erro",
|
||||
"ErrorUrlListImport": "Ocorreu um erro durante a importação da primeira URL na lista. Todas as URLs que não estão mais sendo exibidas foram importadas com sucesso. ",
|
||||
"Events": "Eventos",
|
||||
"Export": "Exportar",
|
||||
"Export_As_ICal": "Exportar período atual para o formato iCal",
|
||||
"Export_Not_Yet_Supported": "Exportação ainda não suportada",
|
||||
"Export_Supported": "Exportação suportada",
|
||||
"Export_To_ICal": "Exportar .ics",
|
||||
"External": "Externo",
|
||||
"ExternalRecipe": "",
|
||||
"ExternalRecipeImport": "Importar receita externa",
|
||||
"ExternalRecipeImportHelp": "Arquivos em pastas sincronizadas em armazenamentos externos não são importados diretamente, mas salvos temporariamente como receitas de importação externa. Aqui, você pode visualizar e editar rapidamente os arquivos recém-encontrados antes que eles sejam movidos para a coleção principal. ",
|
||||
"ExternalStorage": "Armazenamento externo",
|
||||
"External_Recipe_Image": "Imagem externa da receita",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "ID do banco de dados FDC",
|
||||
"FDC_Search": "Busca FDC",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Falha",
|
||||
"Fats": "Gorduras",
|
||||
"File": "Arquivo",
|
||||
"Files": "Arquivos",
|
||||
"FinishedAt": "Finalizado em",
|
||||
"First": "Primeiro",
|
||||
"First_name": "Primeiro Nome",
|
||||
"Food": "Comida",
|
||||
"FoodHelp": "Os alimentos são a base mais importante do Tandoor. Juntamente com as quantidades e suas respectivas unidades, eles compõem os ingredientes das receitas. Eles também podem ser usados para listas de compras, propriedades e muito mais. ",
|
||||
"FoodInherit": "Campos herdados por alimento",
|
||||
"FoodNotOnHand": "Não tem {food} disponível.",
|
||||
"FoodOnHand": "Tem {food} disponível.",
|
||||
"Food_Alias": "Apelido da Comida",
|
||||
"Food_Replace": "Substituir Alimento",
|
||||
"Foods": "Alimentos",
|
||||
"Friday": "Sexta-feira",
|
||||
"Fulltext": "Texto completo",
|
||||
"FulltextHelp": "Campos para pesquisa textual completa. Observação: os métodos de pesquisa 'web', 'phrase' e 'raw' só funcionam com campos de pesquisa textual completa.",
|
||||
"Fuzzy": "Fuzzy",
|
||||
"FuzzySearchHelp": "Use pesquisa fuzzy para encontrar registros mesmo quando existem diferenças na grafia das palavras utilizadas.",
|
||||
"GettingStarted": "Começando",
|
||||
"GroupBy": "Agrupar Por",
|
||||
"HeaderWarning": "Alerta: Mudanças de Cabeçalho apagam a Quantidade/Unidade/Alimento",
|
||||
"Headline": "Título",
|
||||
"Help": "Ajuda",
|
||||
"Hide_External": "Esconder Externo",
|
||||
"Hide_Food": "Esconder Comida",
|
||||
"Hide_Keyword": "Oculta palavras-chave",
|
||||
"Hide_Keywords": "Esconder palavra-chave",
|
||||
"Hide_Recipes": "Esconder Receitas",
|
||||
"Hide_as_header": "Esconder cabeçalho",
|
||||
"Hierarchy": "",
|
||||
"History": "Histórico",
|
||||
"HostedFreeVersion": "Você está utilizando a versão gratuita do Tandoor",
|
||||
"Hour": "Hora",
|
||||
"Hours": "Horas",
|
||||
"Icon": "Ícone",
|
||||
"IgnoreAccents": "Ignorar acentuação",
|
||||
"IgnoreAccentsHelp": "Ignorar acentuação quando pesquisando nos campos selecionados. ",
|
||||
"IgnoreThis": "Nunca auto incluir {food} para compras",
|
||||
"Ignore_Shopping": "Ignorar Mercado",
|
||||
"IgnoredFood": "{food} está definido para ignorar compras.",
|
||||
"Image": "Imagem",
|
||||
"Import": "Importar",
|
||||
"Import Recipe": "Importar Receita",
|
||||
"ImportAll": "Importar todos",
|
||||
"ImportIntoTandoor": "Importar para Tandoor",
|
||||
"Import_Error": "Ocorreu um erro durante a importação. Expanda os detalhes na parte inferior da página para visualizá-los.",
|
||||
"Import_Not_Yet_Supported": "Importação ainda não suportada",
|
||||
"Import_Result_Info": "{imported} de {total} receitas foram importadas",
|
||||
@@ -193,8 +242,11 @@
|
||||
"Imported_From": "Importado de",
|
||||
"Importer_Help": "Mais informações neste importador:",
|
||||
"Information": "Informação",
|
||||
"Ingredient": "Ingrediente",
|
||||
"Ingredient Editor": "Editor de Ingrediente",
|
||||
"Ingredient Overview": "Ingredientes - Visão Geral",
|
||||
"IngredientEditorHelp": "Com o editor de ingredientes você pode editar todos os ingredientes que usam um certo Alimento e/ou unidade ao mesmo tempo. Pode ser utilizado para corrigir erros facilmente, ou para editar várias receitas simultaneamente.",
|
||||
"IngredientHelp": "Ingredientes normalmente são compostos por uma quantidade, uma unidade de medida e um alimento, quantidade e unidade são opcionais. Também pode conter uma anotação, ou ser utilizado como cabeçalho. ",
|
||||
"IngredientInShopping": "Este ingrediente está na sua lista de compras.",
|
||||
"Ingredients": "Ingredientes",
|
||||
"Inherit": "Herdado",
|
||||
@@ -204,31 +256,42 @@
|
||||
"Input": "Entrada",
|
||||
"Instruction_Replace": "Substituir Instrução",
|
||||
"Instructions": "Instruções",
|
||||
"InstructionsEditHelp": "Clique aqui para adicionar instruções. ",
|
||||
"Internal": "Interno",
|
||||
"InviteLinkHelp": "Links para convidar pessoas ao seu espaço. ",
|
||||
"Invite_Link": "Link de convite",
|
||||
"Invites": "Convites",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"Key_Shift": "Shift",
|
||||
"Keyword": "Palavra-chave",
|
||||
"KeywordHelp": "Palavras-chave podem ser utilizadas para organizar sua coleção de receitas.",
|
||||
"Keyword_Alias": "Apelido da palavra-chave",
|
||||
"Keywords": "Palavras-chave",
|
||||
"Language": "Idioma",
|
||||
"Last": "Último",
|
||||
"Last_name": "Último Nome",
|
||||
"Learn_More": "Aprender Mais",
|
||||
"Link": "Link",
|
||||
"Load": "Carregar",
|
||||
"Load_More": "Carregar mais",
|
||||
"Log_Cooking": "Registro de Cozinha",
|
||||
"Log_Recipe_Cooking": "Registrar receitas feitas",
|
||||
"Logo": "Logotipo",
|
||||
"Logout": "Sair",
|
||||
"Make_Header": "Criar cabeçalho",
|
||||
"Make_Ingredient": "Criar Ingrediente",
|
||||
"ManageSubscription": "Gerenciar a inscrição",
|
||||
"Manage_Books": "Gerenciar Livros",
|
||||
"Manage_Emails": "Gerenciar Emails",
|
||||
"MealPlanHelp": "O Plano de Refeição é um calendário usado para planejar refeições. Ele deve conter uma receita ou título e pode ser vinculado à listas de compras. ",
|
||||
"MealPlanShoppingHelp": "Itens na sua lista de compras podem ser vinculados a um Plano de Refeições para organizar sua lista ou atualizar/excluir todos de uma só vez. Ao criar um Plano de Refeições com uma Receita, os ingredientes correspondentes podem ser adicionados automaticamente à Lista de Compras (configurações). ",
|
||||
"Meal_Plan": "Plano de Refeição",
|
||||
"Meal_Plan_Days": "Planos de refeição futuros",
|
||||
"Meal_Type": "Tipo de Comida",
|
||||
"Meal_Type_Required": "Tipo de comida é obrigatório",
|
||||
"Meal_Types": "Tipos de Comida",
|
||||
"Merge": "Mesclar",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Mesclar palavra-chave",
|
||||
"Message": "Mensagem",
|
||||
"MissingProperties": "",
|
||||
@@ -259,6 +322,7 @@
|
||||
"New_Unit": "Nova Unidade",
|
||||
"Next_Day": "Próximo Dia",
|
||||
"Next_Period": "Próximo Período",
|
||||
"No": "",
|
||||
"NoCategory": "Nenhuma categoria selecionada.",
|
||||
"NoMoreUndo": "Nenhuma alteração para desfazer.",
|
||||
"NoUnit": "",
|
||||
@@ -296,6 +360,7 @@
|
||||
"Previous_Day": "Dia Anterior",
|
||||
"Previous_Period": "Período Anterior",
|
||||
"Print": "Imprimir",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Receita privada",
|
||||
"Private_Recipe_Help": "Receita é visível somente para você e para pessoas compartilhadas.",
|
||||
"Properties": "Propriedades",
|
||||
@@ -317,7 +382,9 @@
|
||||
"Recipes": "Receitas",
|
||||
"Recipes_In_Import": "Receitas no seu arquivo de importação",
|
||||
"Recipes_per_page": "Receitas por página",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Remover {food} da sua lista de compras",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Deletar dados nutricionais da receita",
|
||||
"Reset": "Reiniciar",
|
||||
"Reset_Search": "Resetar Busca",
|
||||
@@ -353,6 +420,7 @@
|
||||
"Size": "Tamanho",
|
||||
"Social_Authentication": "Autenticação social",
|
||||
"Sort_by_new": "Ordenar por novos",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Algumas configurações cosméticas podem ser alteradas pelos administradores do espaço e substituirão as configurações do cliente para esse espaço.",
|
||||
"Split_All_Steps": "Divida todas as linhas em etapas separadas.",
|
||||
"StartDate": "Data Início",
|
||||
@@ -379,6 +447,7 @@
|
||||
"Toggle": "Alternar",
|
||||
"Transpose_Words": "Transpor palavras",
|
||||
"Type": "Tipo",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Sem alterações",
|
||||
"Undefined": "Indefinido",
|
||||
"Undo": "Desfazer",
|
||||
@@ -406,6 +475,7 @@
|
||||
"Valid Until": "Válido Até",
|
||||
"View": "Visualizar",
|
||||
"View_Recipes": "Ver Receitas",
|
||||
"Visibility": "",
|
||||
"Waiting": "Espera",
|
||||
"Warning": "Alerta",
|
||||
"Warning_Delete_Supermarket_Category": "Excluir uma categoria de supermercado também excluirá todas as relações com alimentos. Tem certeza?",
|
||||
@@ -414,6 +484,7 @@
|
||||
"Week_Numbers": "Números da Semana",
|
||||
"Welcome": "Bem vindo",
|
||||
"Year": "Ano",
|
||||
"Yes": "",
|
||||
"add_keyword": "Incluir Palavra-Chave",
|
||||
"additional_options": "Opções Adicionais",
|
||||
"advanced": "Avançado",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"Account": "Cont",
|
||||
"Add": "Adaugă",
|
||||
"AddChild": "",
|
||||
"AddFoodToShopping": "Adăugă {food} în lista de cumpărături",
|
||||
"AddToShopping": "Adaugă la lista de cumpărături",
|
||||
"Add_Servings_to_Shopping": "Adăugă {servings} porții la cumpărături",
|
||||
@@ -23,8 +26,13 @@
|
||||
"Auto_Sort_Help": "Mutați toate ingredientele la cel mai potrivit pas.",
|
||||
"Automate": "Automatizat",
|
||||
"Automation": "Automatizare",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Bookmarklet": "Marcaj",
|
||||
"Books": "Cărți",
|
||||
"CREATE_ERROR": "",
|
||||
"Calories": "Calorii",
|
||||
"Cancel": "Anulează",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notele nu pot fi adăugate la lista de cumpărături",
|
||||
@@ -63,6 +71,7 @@
|
||||
"Create_New_Unit": "Adaugă unitate nouă",
|
||||
"Current_Period": "Perioada curentă",
|
||||
"Custom Filter": "Filtru personalizat",
|
||||
"DELETE_ERROR": "",
|
||||
"Date": "Dată",
|
||||
"Day": "Zi",
|
||||
"Days": "Zile",
|
||||
@@ -72,6 +81,7 @@
|
||||
"DelayUntil": "Amână până la",
|
||||
"Delete": "Șterge",
|
||||
"DeleteShoppingConfirm": "Sunteți sigur că doriți să eliminați toate {food} din lista de cumpărături?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_Food": "Ștergere mâncare",
|
||||
"Delete_Keyword": "Ștergere cuvânt cheie",
|
||||
"Description": "Descriere",
|
||||
@@ -80,6 +90,7 @@
|
||||
"Disable_Amount": "Dezactivare cantitate",
|
||||
"Disabled": "Dezactivat",
|
||||
"Documentation": "Documentație",
|
||||
"DontChange": "",
|
||||
"Download": "Descarcă",
|
||||
"Drag_Here_To_Delete": "Mută aici pentru a șterge",
|
||||
"Edit": "Editează",
|
||||
@@ -96,7 +107,9 @@
|
||||
"Export_Supported": "Export compatibil",
|
||||
"Export_To_ICal": "Exportă .ics",
|
||||
"External": "Extern",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Imagine rețetă externă",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Eșec",
|
||||
"Fats": "Grăsimi",
|
||||
"File": "Fișier",
|
||||
@@ -118,6 +131,7 @@
|
||||
"Hide_Keywords": "Ascunde cuvânt cheie",
|
||||
"Hide_Recipes": "Ascunde rețetele",
|
||||
"Hide_as_header": "Ascunderea ca antet",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Oră",
|
||||
"Hours": "Ore",
|
||||
"Icon": "Iconiță",
|
||||
@@ -163,6 +177,7 @@
|
||||
"Log_Recipe_Cooking": "Jurnalul rețetelor de pregătire",
|
||||
"Make_Header": "Creare antet",
|
||||
"Make_Ingredient": "Create ingredient",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Gestionarea cărților",
|
||||
"Manage_Emails": "Gestionarea e-mailurilor",
|
||||
"Meal_Plan": "Plan de alimentare",
|
||||
@@ -171,6 +186,7 @@
|
||||
"Meal_Type_Required": "Tipul mesei este necesar",
|
||||
"Meal_Types": "Tipuri de mese",
|
||||
"Merge": "Unire",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Unește cuvânt cheie",
|
||||
"Message": "Mesaj",
|
||||
"MissingProperties": "",
|
||||
@@ -197,6 +213,7 @@
|
||||
"New_Unit": "Unitate nouă",
|
||||
"Next_Day": "Ziua următoare",
|
||||
"Next_Period": "Perioada următoare",
|
||||
"No": "",
|
||||
"NoCategory": "Nicio categorie selectată.",
|
||||
"NoUnit": "",
|
||||
"No_ID": "ID-ul nu a fost găsit, nu se poate șterge.",
|
||||
@@ -232,6 +249,7 @@
|
||||
"Previous_Day": "Ziua precedentă",
|
||||
"Previous_Period": "Perioada precedentă",
|
||||
"Print": "Tipărește",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Rețetă privată",
|
||||
"Private_Recipe_Help": "Rețeta este arătată doar ție și oamenilor cu care este împărtășită.",
|
||||
"Protected": "Protejat",
|
||||
@@ -248,7 +266,9 @@
|
||||
"Recipes": "Rețete",
|
||||
"Recipes_In_Import": "Rețete în fișierul de import",
|
||||
"Recipes_per_page": "Rețete pe pagină",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Șterge {food} din lista de cumpărături",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Ștergere a nutriției din rețetă",
|
||||
"Reset": "Resetare",
|
||||
"Reset_Search": "Resetarea căutării",
|
||||
@@ -282,6 +302,7 @@
|
||||
"Size": "Marime",
|
||||
"Social_Authentication": "Autentificare socială",
|
||||
"Sort_by_new": "Sortare după nou",
|
||||
"Space": "",
|
||||
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
|
||||
"Starting_Day": "Ziua de început a săptămânii",
|
||||
"StartsWith": "",
|
||||
@@ -309,6 +330,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Tip",
|
||||
"UPDATE_ERROR": "",
|
||||
"Undefined": "Nedefinit",
|
||||
"Unit": "Unitate",
|
||||
"Unit_Alias": "Pseudonim unitate",
|
||||
@@ -330,6 +352,7 @@
|
||||
"Valid Until": "Valabil până la",
|
||||
"View": "Vizualizare",
|
||||
"View_Recipes": "Vizionare rețete",
|
||||
"Visibility": "",
|
||||
"Waiting": "Așteptare",
|
||||
"Warning": "Atenționare",
|
||||
"Warning_Delete_Supermarket_Category": "Ștergerea unei categorii de supermarketuri va șterge, de asemenea, toate relațiile cu alimentele. Sunteți sigur?",
|
||||
@@ -337,6 +360,7 @@
|
||||
"Week": "Săptămână",
|
||||
"Week_Numbers": "Numerele săptămânii",
|
||||
"Year": "An",
|
||||
"Yes": "",
|
||||
"add_keyword": "Adăugare cuvânt cheie",
|
||||
"additional_options": "Opțiuni suplimentare",
|
||||
"advanced": "Avansat",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,19 @@
|
||||
{
|
||||
"AIImportSubtitle": "Använd AI för att importera bilder av recept.",
|
||||
"API": "API",
|
||||
"API_Browser": "",
|
||||
"API_Documentation": "",
|
||||
"AccessTokenHelp": "Nycklar för tillgång till REST API:t.",
|
||||
"Access_Token": "Åtkomstnyckel",
|
||||
"Account": "Konto",
|
||||
"Actions": "Åtgärder",
|
||||
"Activity": "Aktivitet",
|
||||
"Add": "Lägg till",
|
||||
"AddAll": "Lägg till alla",
|
||||
"AddChild": "",
|
||||
"AddFilter": "Lägg till filter",
|
||||
"AddFoodToShopping": "Lägg till {food} på din inköpslista",
|
||||
"AddMany": "Lägg till flera",
|
||||
"AddToShopping": "Lägg till i inköpslista",
|
||||
"Add_Servings_to_Shopping": "Lägg till {servings} portioner till inköp",
|
||||
"Add_Step": "Lägg till steg",
|
||||
@@ -13,25 +24,45 @@
|
||||
"Added_To_Shopping_List": "Lades till i inköpslistan",
|
||||
"Added_by": "Tillagd av",
|
||||
"Added_on": "Tillagd på",
|
||||
"Admin": "Administratör",
|
||||
"Advanced": "Avancerat",
|
||||
"Alignment": "Orientering",
|
||||
"AllRecipes": "Alla recept",
|
||||
"Amount": "Mängd",
|
||||
"App": "App",
|
||||
"Apply": "",
|
||||
"AppImportSubtitle": "Importera din existerande receptdatabas.",
|
||||
"Apply": "Tillämpa",
|
||||
"Are_You_Sure": "Är du säker?",
|
||||
"Auto_Planner": "Autoplanera",
|
||||
"Auto_Sort": "Automatisk Sortering",
|
||||
"Auto_Sort_Help": "Flytta alla ingredienser till det bästa passande steget.",
|
||||
"Automate": "Automatisera",
|
||||
"Automation": "Automatisering",
|
||||
"AutomationHelp": "Automatiseringar låter dig, beroende på typ, tillämpa vissa automatiska ändringar på recept, ingredienser, ... till exempel vid receptimport. ",
|
||||
"Available": "Tillgänglig",
|
||||
"AvailableCategories": "Tillgängliga Kategorier",
|
||||
"Back": "Tillbaka",
|
||||
"BaseUnit": "Basenhet",
|
||||
"BaseUnitHelp": "Standardenhet för automatisk enhetsomvandling",
|
||||
"Basics": "Grunderna",
|
||||
"BatchDeleteConfirm": "",
|
||||
"BatchDeleteHelp": "",
|
||||
"BatchEdit": "",
|
||||
"BatchEditUpdatingItemsCount": "",
|
||||
"Book": "Bok",
|
||||
"Bookmarklet": "Bokmärke",
|
||||
"BookmarkletHelp1": "Dra följande knapp till ditt bokmärkesfält",
|
||||
"BookmarkletHelp2": "Öppna sidan du vill importera från",
|
||||
"BookmarkletHelp3": "Klicka på bokmärket för att utföra importen.",
|
||||
"BookmarkletImportSubtitle": "Använd bokmärket för att importera från icke-publika sidor.",
|
||||
"Books": "Böcker",
|
||||
"CREATE_ERROR": "",
|
||||
"Calculator": "Räknare",
|
||||
"Calories": "Kalorier",
|
||||
"Cancel": "Avbryt",
|
||||
"Cannot_Add_Notes_To_Shopping": "Anteckningar kan inte läggas till inköpslistan",
|
||||
"Carbohydrates": "Kolhydrater",
|
||||
"Cards": "Kort",
|
||||
"Categories": "Kategorier",
|
||||
"Category": "Kategori",
|
||||
"CategoryInstruction": "Dra kategorier för att ändra den ordning som kategorierna visas i inköpslistan.",
|
||||
@@ -47,9 +78,19 @@
|
||||
"Color": "Färg",
|
||||
"Combine_All_Steps": "Kombinera alla steg i ett enda fält.",
|
||||
"Coming_Soon": "Kommer snart",
|
||||
"Comment": "Kommentar",
|
||||
"Comments_setting": "Visa Kommentarer",
|
||||
"Completed": "Avslutad",
|
||||
"Confirm": "Bekräfta",
|
||||
"ConnectorConfig": "Integrationer",
|
||||
"ConnectorConfigHelp": "Med integrationer kan du automatiskt synka data från Tandoor till externa tjänster. ",
|
||||
"Continue": "Fortsätt",
|
||||
"Conversion": "Omvandling",
|
||||
"ConversionsHelp": "Med omvandlingar kan du beräkna mängden av ett livsmedel i olika enheter. För närvarande används detta endast för egenskapsberäkning, senare kan det även användas i andra delar av Tandoor. ",
|
||||
"CookLog": "Tillagningslogg",
|
||||
"CookLogHelp": "Poster i tillagningsloggen för recept. ",
|
||||
"Cooked": "Tillagad",
|
||||
"Copied": "Kopierad",
|
||||
"Copy": "Kopiera",
|
||||
"Copy Link": "Kopiera Länk",
|
||||
"Copy Token": "Kopiera token",
|
||||
@@ -67,6 +108,8 @@
|
||||
"Create_New_Shopping_Category": "Lägg till ny shoppingkategori",
|
||||
"Create_New_Unit": "Lägg till enhet",
|
||||
"Created": "Skapad",
|
||||
"CreatedBy": "Skapad av",
|
||||
"Ctrl+K": "Ctrl+K",
|
||||
"Current_Period": "Nuvarande period",
|
||||
"Custom Filter": "Anpassat filter",
|
||||
"CustomImageHelp": "Ladda upp en bild som visas i överblicken.",
|
||||
@@ -75,7 +118,9 @@
|
||||
"CustomNavLogoHelp": "Ladda upp en bild att använda som meny-logga.",
|
||||
"CustomTheme": "Anpassat tema",
|
||||
"CustomThemeHelp": "Skriv över nuvarande tema genom att ladda upp en anpassad CSS-fil.",
|
||||
"DELETE_ERROR": "",
|
||||
"Data_Import_Info": "Förbättra din samling genom att importera en framtagen lista av livsmedel, enheter och mer för att förbättra din recept-samling.",
|
||||
"Database": "Databas",
|
||||
"Datatype": "Datatyp",
|
||||
"Date": "Datum",
|
||||
"Day": "Dag",
|
||||
@@ -87,6 +132,7 @@
|
||||
"DelayUntil": "Fördröjning till",
|
||||
"Delete": "Radera",
|
||||
"DeleteShoppingConfirm": "Är du säker på att du vill ta bort all {food} från inköpslistan?",
|
||||
"DeleteSomething": "",
|
||||
"Delete_All": "Radera alla",
|
||||
"Delete_Food": "Ta bort livsmedel",
|
||||
"Delete_Keyword": "Ta bort nyckelord",
|
||||
@@ -96,6 +142,7 @@
|
||||
"Disable_Amount": "Inaktivera belopp",
|
||||
"Disabled": "Inaktiverad",
|
||||
"Documentation": "Dokumentation",
|
||||
"DontChange": "",
|
||||
"Download": "Ladda ned",
|
||||
"Drag_Here_To_Delete": "Dra hit för att radera",
|
||||
"Edit": "Redigera",
|
||||
@@ -115,10 +162,12 @@
|
||||
"Export_Supported": "Export stöds",
|
||||
"Export_To_ICal": "Exportera .ics",
|
||||
"External": "Extern",
|
||||
"ExternalRecipe": "",
|
||||
"External_Recipe_Image": "Extern receptbild",
|
||||
"FDC_ID": "FDC ID",
|
||||
"FDC_ID_help": "FDC databas ID",
|
||||
"FDC_Search": "FDC Sök",
|
||||
"FETCH_ERROR": "",
|
||||
"Failure": "Misslyckas",
|
||||
"Fats": "Fett",
|
||||
"File": "Fil",
|
||||
@@ -141,6 +190,7 @@
|
||||
"Hide_Keywords": "Dölj nyckelord",
|
||||
"Hide_Recipes": "Dölj recept",
|
||||
"Hide_as_header": "Göm som rubrik",
|
||||
"Hierarchy": "",
|
||||
"Hour": "Timme",
|
||||
"Hours": "Timmar",
|
||||
"Icon": "Ikon",
|
||||
@@ -189,6 +239,7 @@
|
||||
"Logo": "Logga",
|
||||
"Make_Header": "Skapa rubrik",
|
||||
"Make_Ingredient": "Skapa ingrediens",
|
||||
"ManageSubscription": "",
|
||||
"Manage_Books": "Hantera böcker",
|
||||
"Manage_Emails": "Hantera mejladresser",
|
||||
"Meal_Plan": "Måltidsplanering",
|
||||
@@ -197,6 +248,7 @@
|
||||
"Meal_Type_Required": "Måltidstyp är obligatorisk",
|
||||
"Meal_Types": "Måltidstyper",
|
||||
"Merge": "Slå samman",
|
||||
"MergeAutomateHelp": "",
|
||||
"Merge_Keyword": "Slå samman nyckelord",
|
||||
"Message": "Meddelande",
|
||||
"MissingProperties": "",
|
||||
@@ -227,6 +279,7 @@
|
||||
"New_Unit": "Ny enhet",
|
||||
"Next_Day": "Nästa dag",
|
||||
"Next_Period": "Nästa period",
|
||||
"No": "",
|
||||
"NoCategory": "Ingen kategori vald.",
|
||||
"NoMoreUndo": "Inga ändringar att ångra.",
|
||||
"NoUnit": "",
|
||||
@@ -267,6 +320,7 @@
|
||||
"Previous_Day": "Föregående dag",
|
||||
"Previous_Period": "Föregående period",
|
||||
"Print": "Skriv ut",
|
||||
"Private": "",
|
||||
"Private_Recipe": "Privat Recept",
|
||||
"Private_Recipe_Help": "Receptet visas bara för dig och personer som det delas med.",
|
||||
"Properties": "Egenskaper",
|
||||
@@ -288,7 +342,9 @@
|
||||
"Recipes": "Recept",
|
||||
"Recipes_In_Import": "Recept i din importfil",
|
||||
"Recipes_per_page": "Recept per sida",
|
||||
"RemoveAllType": "",
|
||||
"RemoveFoodFromShopping": "Ta bort {mat} från din inköpslista",
|
||||
"RemoveParent": "",
|
||||
"Remove_nutrition_recipe": "Ta bort näring från receptet",
|
||||
"Reset": "Återställ",
|
||||
"Reset_Search": "Rensa sök",
|
||||
@@ -327,6 +383,7 @@
|
||||
"Size": "Storlek",
|
||||
"Social_Authentication": "Social autentisering",
|
||||
"Sort_by_new": "Sortera efter ny",
|
||||
"Space": "",
|
||||
"Space_Cosmetic_Settings": "Vissa kosmetiska inställningar kan ändras av hushålls-administratörer och skriver över klientinställningar för det hushållet.",
|
||||
"Split_All_Steps": "Dela upp alla rader i separata steg.",
|
||||
"StartDate": "Startdatum",
|
||||
@@ -357,6 +414,7 @@
|
||||
"TrigramThreshold": "",
|
||||
"TrigramThresholdHelp": "",
|
||||
"Type": "Typ",
|
||||
"UPDATE_ERROR": "",
|
||||
"Unchanged": "Oförändrad",
|
||||
"Undefined": "Odefinierad",
|
||||
"Undo": "Ångra",
|
||||
@@ -384,6 +442,7 @@
|
||||
"Valid Until": "Giltig till",
|
||||
"View": "Visa",
|
||||
"View_Recipes": "Visa recept",
|
||||
"Visibility": "",
|
||||
"Waiting": "Väntan",
|
||||
"Warning": "Varning",
|
||||
"Warning_Delete_Supermarket_Category": "Om du tar bort en mataffärskategori raderas också alla relationer till livsmedel. Är du säker?",
|
||||
@@ -392,6 +451,7 @@
|
||||
"Week_Numbers": "Veckonummer",
|
||||
"Welcome": "Välkommen",
|
||||
"Year": "År",
|
||||
"Yes": "",
|
||||
"add_keyword": "Lägg till nyckelord",
|
||||
"additional_options": "Ytterligare alternativ",
|
||||
"advanced": "Avancerat",
|
||||
@@ -489,7 +549,7 @@
|
||||
"merge_selection": "Ersätt alla förekomster av {source} med den valda {type}.",
|
||||
"merge_title": "Slå samman {type}",
|
||||
"min": "min",
|
||||
"ml": "millimeter [ml] (metrisk, volym)",
|
||||
"ml": "milliliter [ml] (metrisk, volym)",
|
||||
"move_confirmation": "Flytta<i>{child}</i> till förälder <i>{parent}</i>",
|
||||
"move_selection": "Välj en förälder {type} att flytta {source} till.",
|
||||
"move_title": "Flytta {type}",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user