mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-05 06:08:46 -05:00
Merge branch 'develop' into feature/vue3
# Conflicts: # requirements.txt
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
node-version: ["18"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: awalsh128/cache-apt-pkgs-action@v1.4.1
|
||||
- uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||
with:
|
||||
packages: libsasl2-dev python3-dev libldap2-dev libssl-dev
|
||||
version: 1.0
|
||||
|
||||
@@ -6,6 +6,8 @@ RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg li
|
||||
#Print all logs without buffering it.
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
ENV DOCKER true
|
||||
|
||||
#This port will be used by gunicorn.
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -33,6 +35,12 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de
|
||||
#Copy project and execute it.
|
||||
COPY . ./
|
||||
|
||||
# collect the static files
|
||||
RUN /opt/recipes/venv/bin/python manage.py collectstatic_js_reverse
|
||||
RUN /opt/recipes/venv/bin/python manage.py collectstatic --noinput
|
||||
# copy the collected static files to a different location, so they can be moved into a potentially mounted volume
|
||||
RUN mv /opt/recipes/staticfiles /opt/recipes/staticfiles-collect
|
||||
|
||||
# collect information from git repositories
|
||||
RUN /opt/recipes/venv/bin/python version.py
|
||||
# delete git repositories to reduce image size
|
||||
|
||||
11
boot.sh
11
boot.sh
@@ -67,12 +67,21 @@ echo "Migrating database"
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
echo "Generating static files"
|
||||
if [[ "${DOCKER}" == "true" ]]; then
|
||||
echo "Copying cached static files from docker build"
|
||||
|
||||
mkdir -p /opt/recipes/staticfiles
|
||||
rm -rf /opt/recipes/staticfiles/*
|
||||
mv /opt/recipes/staticfiles-collect/* /opt/recipes/staticfiles
|
||||
rm -rf /opt/recipes/staticfiles-collect
|
||||
else
|
||||
echo "Collecting static files, this may take a while..."
|
||||
|
||||
python manage.py collectstatic_js_reverse
|
||||
python manage.py collectstatic --noinput
|
||||
|
||||
echo "Done"
|
||||
fi
|
||||
|
||||
chmod -R 755 /opt/recipes/mediafiles
|
||||
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
import logging
|
||||
from logging import Logger
|
||||
from typing import Dict, Tuple
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from homeassistant_api import Client, HomeassistantAPIError, Domain
|
||||
from aiohttp import ClientError, request
|
||||
|
||||
from cookbook.connectors.connector import Connector
|
||||
from cookbook.models import ShoppingListEntry, ConnectorConfig, Space
|
||||
|
||||
|
||||
class HomeAssistant(Connector):
|
||||
_domains_cache: dict[str, Domain]
|
||||
_config: ConnectorConfig
|
||||
_logger: Logger
|
||||
_client: Client
|
||||
|
||||
def __init__(self, config: ConnectorConfig):
|
||||
if not config.token or not config.url or not config.todo_entity:
|
||||
raise ValueError("config for HomeAssistantConnector in incomplete")
|
||||
|
||||
self._domains_cache = dict()
|
||||
if config.url[-1] != "/":
|
||||
config.url += "/"
|
||||
self._config = config
|
||||
self._logger = logging.getLogger("connector.HomeAssistant")
|
||||
self._client = Client(self._config.url, self._config.token, async_cache_session=False, use_async=True)
|
||||
|
||||
async def homeassistant_api_call(self, method: str, path: str, data: Dict) -> str:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self._config.token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
async with request(method, urljoin(self._config.url, path), headers=headers, json=data) as response:
|
||||
response.raise_for_status()
|
||||
return await response.json()
|
||||
|
||||
async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
|
||||
if not self._config.on_shopping_list_entry_created_enabled:
|
||||
@@ -28,15 +37,17 @@ class HomeAssistant(Connector):
|
||||
|
||||
item, description = _format_shopping_list_entry(shopping_list_entry)
|
||||
|
||||
todo_domain = self._domains_cache.get('todo')
|
||||
try:
|
||||
if todo_domain is None:
|
||||
todo_domain = await self._client.async_get_domain('todo')
|
||||
self._domains_cache['todo'] = todo_domain
|
||||
logging.debug(f"adding {item=} to {self._config.name}")
|
||||
|
||||
logging.debug(f"pushing {item} to {self._config.name}")
|
||||
await todo_domain.add_item(entity_id=self._config.todo_entity, item=item)
|
||||
except HomeassistantAPIError as err:
|
||||
data = {
|
||||
"entity_id": self._config.todo_entity,
|
||||
"item": item,
|
||||
"description": description,
|
||||
}
|
||||
|
||||
try:
|
||||
await self.homeassistant_api_call("POST", "services/todo/add_item", data)
|
||||
except ClientError as err:
|
||||
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
|
||||
|
||||
async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
|
||||
@@ -48,24 +59,31 @@ class HomeAssistant(Connector):
|
||||
if not self._config.on_shopping_list_entry_deleted_enabled:
|
||||
return
|
||||
|
||||
item, description = _format_shopping_list_entry(shopping_list_entry)
|
||||
if not hasattr(shopping_list_entry._state.fields_cache, "food"):
|
||||
# Sometimes the food foreign key is not loaded, and we cant load it from an async process
|
||||
self._logger.debug("required property was not present in ShoppingListEntry")
|
||||
return
|
||||
|
||||
item, _ = _format_shopping_list_entry(shopping_list_entry)
|
||||
|
||||
logging.debug(f"removing {item=} from {self._config.name}")
|
||||
|
||||
data = {
|
||||
"entity_id": self._config.todo_entity,
|
||||
"item": item,
|
||||
}
|
||||
|
||||
todo_domain = self._domains_cache.get('todo')
|
||||
try:
|
||||
if todo_domain is None:
|
||||
todo_domain = await self._client.async_get_domain('todo')
|
||||
self._domains_cache['todo'] = todo_domain
|
||||
|
||||
logging.debug(f"deleting {item} from {self._config.name}")
|
||||
await todo_domain.remove_item(entity_id=self._config.todo_entity, item=item)
|
||||
except HomeassistantAPIError as err:
|
||||
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
|
||||
await self.homeassistant_api_call("POST", "services/todo/remove_item", data)
|
||||
except ClientError as err:
|
||||
# This error will always trigger if the item is not present/found
|
||||
self._logger.debug(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
|
||||
|
||||
async def close(self) -> None:
|
||||
await self._client.async_cache_session.close()
|
||||
pass
|
||||
|
||||
|
||||
def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry):
|
||||
def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry) -> Tuple[str, str]:
|
||||
item = shopping_list_entry.food.name
|
||||
if shopping_list_entry.amount > 0:
|
||||
item += f" ({shopping_list_entry.amount:.2f}".rstrip('0').rstrip('.')
|
||||
@@ -76,10 +94,10 @@ def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry):
|
||||
else:
|
||||
item += ")"
|
||||
|
||||
description = "Imported by TandoorRecipes"
|
||||
description = "From TandoorRecipes"
|
||||
if shopping_list_entry.created_by.first_name and len(shopping_list_entry.created_by.first_name) > 0:
|
||||
description += f", created by {shopping_list_entry.created_by.first_name}"
|
||||
description += f", by {shopping_list_entry.created_by.first_name}"
|
||||
else:
|
||||
description += f", created by {shopping_list_entry.created_by.username}"
|
||||
description += f", by {shopping_list_entry.created_by.username}"
|
||||
|
||||
return item, description
|
||||
|
||||
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-21 14:39+0100\n"
|
||||
"PO-Revision-Date: 2023-09-25 09:59+0000\n"
|
||||
"Last-Translator: Matias Laporte <laportematias+weblate@gmail.com>\n"
|
||||
"PO-Revision-Date: 2024-03-27 19:02+0000\n"
|
||||
"Last-Translator: Axel Breiterman <axelbreiterman@gmail.com>\n"
|
||||
"Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/es/>\n"
|
||||
"Language: es\n"
|
||||
@@ -23,7 +23,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
"X-Generator: Weblate 5.4.2\n"
|
||||
|
||||
#: .\cookbook\forms.py:45
|
||||
msgid ""
|
||||
@@ -97,14 +97,16 @@ msgid ""
|
||||
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
|
||||
"profile\">Long Lived Access Token</a> for your HomeAssistant instance"
|
||||
msgstr ""
|
||||
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
|
||||
"profile\">Token de larga duración</a>para tu instancia de HomeAssistant"
|
||||
|
||||
#: .\cookbook\forms.py:193
|
||||
msgid "Something like http://homeassistant.local:8123/api"
|
||||
msgstr ""
|
||||
msgstr "Algo similar a http://homeassistant.local:8123/api"
|
||||
|
||||
#: .\cookbook\forms.py:205
|
||||
msgid "http://homeassistant.local:8123/api for example"
|
||||
msgstr ""
|
||||
msgstr "por ejemplo http://homeassistant.local:8123/api for example"
|
||||
|
||||
#: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117
|
||||
msgid "Storage"
|
||||
@@ -279,7 +281,7 @@ msgstr "Ha alcanzado el número máximo de recetas para su espacio."
|
||||
|
||||
#: .\cookbook\helper\permission_helper.py:414
|
||||
msgid "You have more users than allowed in your space."
|
||||
msgstr ""
|
||||
msgstr "Tenés mas usuarios que los permitidos en tu espacio"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:304
|
||||
#, fuzzy
|
||||
@@ -309,7 +311,7 @@ msgstr "fermentar"
|
||||
|
||||
#: .\cookbook\helper\recipe_url_import.py:310
|
||||
msgid "sous-vide"
|
||||
msgstr ""
|
||||
msgstr "sous-vide"
|
||||
|
||||
#: .\cookbook\helper\shopping_helper.py:150
|
||||
msgid "You must supply a servings size"
|
||||
@@ -318,7 +320,7 @@ msgstr "Debe proporcionar un tamaño de porción"
|
||||
#: .\cookbook\helper\template_helper.py:95
|
||||
#: .\cookbook\helper\template_helper.py:97
|
||||
msgid "Could not parse template code."
|
||||
msgstr ""
|
||||
msgstr "No se pudo parsear el código de la planitlla."
|
||||
|
||||
#: .\cookbook\integration\copymethat.py:44
|
||||
#: .\cookbook\integration\melarecipes.py:37
|
||||
@@ -342,6 +344,8 @@ msgid ""
|
||||
"An unexpected error occurred during the import. Please make sure you have "
|
||||
"uploaded a valid file."
|
||||
msgstr ""
|
||||
"Ocurrió un error inesperado al importar. Por favor asegurate de haber subido "
|
||||
"un archivo válido."
|
||||
|
||||
#: .\cookbook\integration\integration.py:217
|
||||
msgid "The following recipes were ignored because they already existed:"
|
||||
@@ -457,7 +461,7 @@ msgstr "Calorías"
|
||||
|
||||
#: .\cookbook\migrations\0190_auto_20230525_1506.py:20
|
||||
msgid "kcal"
|
||||
msgstr ""
|
||||
msgstr "kcal"
|
||||
|
||||
#: .\cookbook\models.py:325
|
||||
msgid ""
|
||||
|
||||
@@ -11,8 +11,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-21 14:39+0100\n"
|
||||
"PO-Revision-Date: 2024-03-03 23:19+0000\n"
|
||||
"Last-Translator: M Ugur <mugurd@gmail.com>\n"
|
||||
"PO-Revision-Date: 2024-04-01 22:04+0000\n"
|
||||
"Last-Translator: atom karinca <atomkarinca@tutanota.com>\n"
|
||||
"Language-Team: Turkish <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/tr/>\n"
|
||||
"Language: tr\n"
|
||||
@@ -20,7 +20,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
"X-Generator: Weblate 5.4.2\n"
|
||||
|
||||
#: .\cookbook\forms.py:45
|
||||
msgid ""
|
||||
@@ -63,42 +63,48 @@ msgid ""
|
||||
"To prevent duplicates recipes with the same name as existing ones are "
|
||||
"ignored. Check this box to import everything."
|
||||
msgstr ""
|
||||
"Varolan tariflerden benzer isimli olanlar mükerrerliği engellemek için "
|
||||
"gözardı edilecektir. Tümünü içeri aktarmak için bu kutucuğu işaretleyin."
|
||||
|
||||
#: .\cookbook\forms.py:143
|
||||
msgid "Add your comment: "
|
||||
msgstr ""
|
||||
msgstr "Yorum ekleyin: "
|
||||
|
||||
#: .\cookbook\forms.py:151
|
||||
msgid "Leave empty for dropbox and enter app password for nextcloud."
|
||||
msgstr ""
|
||||
msgstr "Dropbox için boş bırakın ve Nextcloud için uygulama şifresini girin."
|
||||
|
||||
#: .\cookbook\forms.py:154
|
||||
msgid "Leave empty for nextcloud and enter api token for dropbox."
|
||||
msgstr ""
|
||||
msgstr "Nextcloud için boş bırakın ve Dropbox için API anahtarını girin."
|
||||
|
||||
#: .\cookbook\forms.py:160
|
||||
msgid ""
|
||||
"Leave empty for dropbox and enter only base url for nextcloud (<code>/remote."
|
||||
"php/webdav/</code> is added automatically)"
|
||||
msgstr ""
|
||||
"Dropbox için boş bırakın ve Nextcloud için yalnızca ana URL'yi "
|
||||
"girin(<code>/remote.php/webdav/</code> otomatik olarak eklenir)"
|
||||
|
||||
#: .\cookbook\forms.py:188
|
||||
msgid ""
|
||||
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
|
||||
"profile\">Long Lived Access Token</a> for your HomeAssistant instance"
|
||||
msgstr ""
|
||||
"HomeAssistant uygulamanız için <a href=\"https://www.home-assistant.io/docs/"
|
||||
"authentication/#your-account-profile\">Uzun Süreli Erişim Anahtarı</a>"
|
||||
|
||||
#: .\cookbook\forms.py:193
|
||||
msgid "Something like http://homeassistant.local:8123/api"
|
||||
msgstr ""
|
||||
msgstr "Örneğin http://homeassistant.local:8123/api"
|
||||
|
||||
#: .\cookbook\forms.py:205
|
||||
msgid "http://homeassistant.local:8123/api for example"
|
||||
msgstr ""
|
||||
msgstr "http://homeassistant.local:8123/api örneğin"
|
||||
|
||||
#: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117
|
||||
msgid "Storage"
|
||||
msgstr ""
|
||||
msgstr "Depolama"
|
||||
|
||||
#: .\cookbook\forms.py:222
|
||||
msgid "Active"
|
||||
@@ -106,51 +112,60 @@ msgstr "Aktif"
|
||||
|
||||
#: .\cookbook\forms.py:226
|
||||
msgid "Search String"
|
||||
msgstr ""
|
||||
msgstr "Arama Sorgusu"
|
||||
|
||||
#: .\cookbook\forms.py:246
|
||||
msgid "File ID"
|
||||
msgstr ""
|
||||
msgstr "Dosya ID"
|
||||
|
||||
#: .\cookbook\forms.py:262
|
||||
msgid "Maximum number of users for this space reached."
|
||||
msgstr ""
|
||||
msgstr "Bu alan için maksimum kullanıcı sayısına ulaşıldı."
|
||||
|
||||
#: .\cookbook\forms.py:268
|
||||
msgid "Email address already taken!"
|
||||
msgstr ""
|
||||
msgstr "Email adresi zaten alınmış!"
|
||||
|
||||
#: .\cookbook\forms.py:275
|
||||
msgid ""
|
||||
"An email address is not required but if present the invite link will be sent "
|
||||
"to the user."
|
||||
msgstr ""
|
||||
"Email adresi zorunlu değildir fakat verilmesi halinde davet linki "
|
||||
"kullanıcıya gönderilecektir."
|
||||
|
||||
#: .\cookbook\forms.py:287
|
||||
msgid "Name already taken."
|
||||
msgstr ""
|
||||
msgstr "İsim zaten alınmış."
|
||||
|
||||
#: .\cookbook\forms.py:298
|
||||
msgid "Accept Terms and Privacy"
|
||||
msgstr ""
|
||||
msgstr "Koşulları ve Gizliliği Onayla"
|
||||
|
||||
#: .\cookbook\forms.py:332
|
||||
msgid ""
|
||||
"Determines how fuzzy a search is if it uses trigram similarity matching (e."
|
||||
"g. low values mean more typos are ignored)."
|
||||
msgstr ""
|
||||
"Trigram benzerlik eşleşmesi kullanılması halinde aramanın ne kadar bulanık "
|
||||
"olduğunu belirler (ör. düşük değerler daha fazla yazım hatasını gözardı "
|
||||
"eder)."
|
||||
|
||||
#: .\cookbook\forms.py:340
|
||||
msgid ""
|
||||
"Select type method of search. Click <a href=\"/docs/search/\">here</a> for "
|
||||
"full description of choices."
|
||||
msgstr ""
|
||||
"Arama tipi metodunu seçin. Seçeneklerin tam açıklamasını görmek için <a "
|
||||
"href=\"/docs/search/\">buraya</a> tıklayın."
|
||||
|
||||
#: .\cookbook\forms.py:341
|
||||
msgid ""
|
||||
"Use fuzzy matching on units, keywords and ingredients when editing and "
|
||||
"importing recipes."
|
||||
msgstr ""
|
||||
"Tarifleri düzenlerken ve içeri aktarırken birimler, anahtar kelimeler ve "
|
||||
"malzemelerde bulanık eşleştirme kullan."
|
||||
|
||||
#: .\cookbook\forms.py:342
|
||||
msgid ""
|
||||
|
||||
@@ -63,6 +63,8 @@ Modify the below to match your environment and add it to your `docker-compose.ym
|
||||
``` yaml
|
||||
pgbackup:
|
||||
container_name: pgbackup
|
||||
env_file:
|
||||
- ./.env
|
||||
environment:
|
||||
BACKUP_KEEP_DAYS: "8"
|
||||
BACKUP_KEEP_MONTHS: "6"
|
||||
|
||||
@@ -2,7 +2,7 @@ Django==4.2.11
|
||||
cryptography===42.0.5
|
||||
django-annoying==0.10.6
|
||||
django-cleanup==8.0.0
|
||||
django-crispy-forms==2.0
|
||||
django-crispy-forms==2.1
|
||||
crispy-bootstrap4==2022.1
|
||||
django-tables2==2.7.0
|
||||
djangorestframework==3.14.0
|
||||
@@ -10,12 +10,12 @@ drf-writable-nested==0.7.0
|
||||
drf-spectacular==0.27.1
|
||||
drf-spectacular-sidecar==2024.2.1
|
||||
django-oauth-toolkit==2.3.0
|
||||
django-debug-toolbar==4.2.0
|
||||
django-debug-toolbar==4.3.0
|
||||
bleach==6.0.0
|
||||
gunicorn==21.2.0
|
||||
lxml==5.1.0
|
||||
Markdown==3.5.1
|
||||
Pillow==10.2.0
|
||||
Pillow==10.3.0
|
||||
psycopg2-binary==2.9.9
|
||||
python-dotenv==1.0.0
|
||||
requests==2.31.0
|
||||
@@ -25,14 +25,14 @@ whitenoise==6.6.0
|
||||
icalendar==5.0.11
|
||||
pyyaml==6.0.1
|
||||
uritemplate==4.1.1
|
||||
beautifulsoup4==4.12.2
|
||||
beautifulsoup4==4.12.3
|
||||
microdata==0.8.0
|
||||
mock==5.1.0
|
||||
Jinja2==3.1.3
|
||||
django-webpack-loader==3.0.1
|
||||
git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82
|
||||
django-allauth==0.61.1
|
||||
recipe-scrapers==14.52.0
|
||||
recipe-scrapers==14.55.0
|
||||
django-scopes==2.0.0
|
||||
django-treebeard==4.7
|
||||
django-cors-headers==4.3.1
|
||||
@@ -45,13 +45,13 @@ django-auth-ldap==4.6.0
|
||||
pyppeteer==2.0.0
|
||||
validators==0.20.0
|
||||
pytube==15.0.0
|
||||
homeassistant-api==4.1.1.post2
|
||||
django-vite==3.0.3
|
||||
aiohttp==3.9.3
|
||||
|
||||
# Development
|
||||
pytest==8.0.0
|
||||
pytest-django==4.8.0
|
||||
pytest-cov===4.1.0
|
||||
pytest-cov===5.0.0
|
||||
pytest-factoryboy==2.6.0
|
||||
pytest-html==4.1.1
|
||||
pytest-asyncio==0.23.5
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
text-field="name"
|
||||
value-field="id"
|
||||
v-model="food.supermarket_category"
|
||||
@change="detail_modal_visible = false; updateFoodCategory(food)"
|
||||
@input="detail_modal_visible = false; updateFoodCategory(food)"
|
||||
></b-form-select>
|
||||
|
||||
<b-button variant="info" block
|
||||
|
||||
@@ -4,38 +4,38 @@
|
||||
"err_creating_resource": "Kaynak oluşturulurken bir hata oluştu!",
|
||||
"err_updating_resource": "Kaynak güncellenirken bir hata oluştu!",
|
||||
"err_deleting_resource": "Kaynak silinirken bir hata oluştu!",
|
||||
"err_deleting_protected_resource": "",
|
||||
"err_moving_resource": "",
|
||||
"err_merging_resource": "",
|
||||
"err_deleting_protected_resource": "Silmeye çalıştığınız nesne hala kullanılıyor ve silinemedi.",
|
||||
"err_moving_resource": "Kaynak taşınırken bir hata oluştu!",
|
||||
"err_merging_resource": "Kaynak birleştirilirken bir hata oluştu!",
|
||||
"success_fetching_resource": "Kaynak başarıyla getirildi!",
|
||||
"success_creating_resource": "Kaynak başarıyla oluşturuldu!",
|
||||
"success_updating_resource": "",
|
||||
"success_updating_resource": "Kaynak başarıyla güncellendi!",
|
||||
"success_deleting_resource": "Kaynak başarıyla silindi!",
|
||||
"success_moving_resource": "Kaynak başarıyla taşındı!",
|
||||
"success_merging_resource": "Kaynak başarıyla birleştirildi!",
|
||||
"file_upload_disabled": "Alanınız için dosya yükleme aktif değil.",
|
||||
"warning_space_delete": "Tüm tarifler, alışveriş listeleri, yemek planları ve oluşturduğunuz her şey dahil olmak üzere silinecektir. Bu geri alınamaz! Bunu yapmak istediğinizden emin misiniz?",
|
||||
"food_inherit_info": "",
|
||||
"warning_space_delete": "Tüm tarifleri, alışveriş listelerini, yemek planlarını ve oluşturduğunuz diğer her şeyi içeren alanınızı silebilirsiniz. Bu geri alınamaz! Bunu yapmak istediğinizden emin misiniz?",
|
||||
"food_inherit_info": "Yiyeceklerdeki öntanımlı olarak aktarılması gereken alanlar.",
|
||||
"step_time_minutes": "Dakika olarak adım süresi",
|
||||
"confirm_delete": "",
|
||||
"import_running": "",
|
||||
"all_fields_optional": "",
|
||||
"confirm_delete": "Bu {object}'yi silmek istediğinizden emin misiniz?",
|
||||
"import_running": "İçeri aktarım devam ediyor, lütfen bekleyin!",
|
||||
"all_fields_optional": "Bütün alanlar tercihe bağlıdır ve boş bırakılabilir.",
|
||||
"convert_internal": "Dahili tarif'e dönüştür",
|
||||
"show_only_internal": "Sadece dahili tarifler",
|
||||
"show_only_internal": "Sadece dahili tarifleri göster",
|
||||
"show_split_screen": "Bölünmüş Görünüm",
|
||||
"Log_Recipe_Cooking": "",
|
||||
"External_Recipe_Image": "",
|
||||
"Log_Recipe_Cooking": "Günlük Tarif Pişirme",
|
||||
"External_Recipe_Image": "Harici Tarif Resim",
|
||||
"Add_to_Shopping": "Alışverişe Ekle",
|
||||
"Add_to_Plan": "",
|
||||
"Step_start_time": "",
|
||||
"Add_to_Plan": "Plana ekle",
|
||||
"Step_start_time": "Adım başlangıç zamanı",
|
||||
"Sort_by_new": "Yeniye göre sırala",
|
||||
"Table_of_Contents": "İçindekiler Tablosu",
|
||||
"Recipes_per_page": "Sayfa Başına Tarif",
|
||||
"Show_as_header": "Başlığı Göster",
|
||||
"Hide_as_header": "Başlığı gizle",
|
||||
"Add_nutrition_recipe": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Copy_template_reference": "",
|
||||
"Show_as_header": "Başlık olarak göster",
|
||||
"Hide_as_header": "Başlık olarak gizle",
|
||||
"Add_nutrition_recipe": "Tarife besin değeri ekle",
|
||||
"Remove_nutrition_recipe": "Tariften besin değeri sil",
|
||||
"Copy_template_reference": "Şablon referansını kopyala",
|
||||
"Save_and_View": "Kaydet & Görüntüle",
|
||||
"Manage_Books": "Kitapları Yönet",
|
||||
"Meal_Plan": "Yemek Planı",
|
||||
@@ -44,12 +44,12 @@
|
||||
"Recipe_Image": "Tarif Resmi",
|
||||
"Import_finished": "İçeriye Aktarma Bitti",
|
||||
"View_Recipes": "Tarifleri Görüntüle",
|
||||
"Log_Cooking": "",
|
||||
"Log_Cooking": "Günlük Pişirme",
|
||||
"New_Recipe": "Yeni Tarif",
|
||||
"Url_Import": "Url İçeri Aktar",
|
||||
"Reset_Search": "Aramayı Sıfırla",
|
||||
"Recently_Viewed": "Son Görüntülenen",
|
||||
"Load_More": "Daha Fazla",
|
||||
"Load_More": "Daha Fazla Yükle",
|
||||
"New_Keyword": "Yeni Anahtar Kelime",
|
||||
"Delete_Keyword": "Anahtar Kelimeyi Sil",
|
||||
"Edit_Keyword": "Anahtar Kelimeyi Düzenle",
|
||||
@@ -57,20 +57,20 @@
|
||||
"Move_Keyword": "Anahtar Kelimeyi Taşı",
|
||||
"Merge_Keyword": "Anahtar Kelimeyi Birleştir",
|
||||
"Hide_Keywords": "Anahtar Kelimeyi Gizle",
|
||||
"Hide_Recipes": "Tarifi Gizle",
|
||||
"Hide_Recipes": "Tarifleri Gizle",
|
||||
"Move_Up": "Yukarı Taşı",
|
||||
"Move_Down": "Aşağıya Taşı",
|
||||
"Step_Name": "Adım Adı",
|
||||
"Step_Type": "Adım Tipi",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"Make_Header": "Başlık Oluştur",
|
||||
"Make_Ingredient": "Malzeme Oluştur",
|
||||
"Enable_Amount": "Tutarı Etkinleştir",
|
||||
"Disable_Amount": "Tutarı Devre Dışı Bırak",
|
||||
"Ingredient Editor": "",
|
||||
"Ingredient Editor": "Malzeme Düzenleyici",
|
||||
"Private_Recipe": "Özel Tarif",
|
||||
"Private_Recipe_Help": "",
|
||||
"reusable_help_text": "",
|
||||
"Add_Step": "",
|
||||
"Private_Recipe_Help": "Tarif yalnızca size ve paylaştığınız kişilere gösterilir.",
|
||||
"reusable_help_text": "Davet bağlantısı birden fazla kullanıcı için kullanılabilir olsun mu.",
|
||||
"Add_Step": "Adım Ekle",
|
||||
"Keywords": "Anahtar Kelimeler",
|
||||
"Books": "Kitaplar",
|
||||
"Proteins": "Proteinler",
|
||||
@@ -78,7 +78,7 @@
|
||||
"Carbohydrates": "Karbonhidratlar",
|
||||
"Calories": "Kaloriler",
|
||||
"Energy": "Enerji",
|
||||
"Nutrition": "Besin",
|
||||
"Nutrition": "Besin Değeri",
|
||||
"Date": "Tarih",
|
||||
"Share": "Paylaş",
|
||||
"Automation": "Otomasyon",
|
||||
@@ -93,18 +93,18 @@
|
||||
"New": "Yeni",
|
||||
"Note": "Not",
|
||||
"Success": "Başarılı",
|
||||
"Failure": "Hata",
|
||||
"Failure": "Başarısız",
|
||||
"Protected": "Korumalı",
|
||||
"Ingredients": "Mazemeler",
|
||||
"Ingredients": "Malzemeler",
|
||||
"Supermarket": "Market",
|
||||
"Categories": "Kategoriler",
|
||||
"Category": "Kategori",
|
||||
"Selected": "Seçilen",
|
||||
"min": "",
|
||||
"Servings": "",
|
||||
"Waiting": "",
|
||||
"Preparation": "",
|
||||
"External": "",
|
||||
"min": "min",
|
||||
"Servings": "Servis Sayısı",
|
||||
"Waiting": "Bekleniyor",
|
||||
"Preparation": "Hazırlama",
|
||||
"External": "Harici",
|
||||
"Size": "Boyut",
|
||||
"Files": "Dosyalar",
|
||||
"File": "Dosya",
|
||||
@@ -112,7 +112,7 @@
|
||||
"Image": "Resim",
|
||||
"Delete": "Sil",
|
||||
"Open": "Aç",
|
||||
"Ok": "Aç",
|
||||
"Ok": "Tamam",
|
||||
"Save": "Kaydet",
|
||||
"Step": "Adım",
|
||||
"Search": "Ara",
|
||||
@@ -121,7 +121,7 @@
|
||||
"Settings": "Ayarlar",
|
||||
"or": "veya",
|
||||
"and": "ve",
|
||||
"Information": "bilgi",
|
||||
"Information": "Bilgi",
|
||||
"Download": "İndir",
|
||||
"Create": "Oluştur",
|
||||
"Search Settings": "Arama Ayarları",
|
||||
@@ -129,10 +129,10 @@
|
||||
"Recipes": "Tarifler",
|
||||
"Move": "Taşı",
|
||||
"Merge": "Birleştir",
|
||||
"Parent": "",
|
||||
"Copy Link": "",
|
||||
"Copy Token": "",
|
||||
"delete_confirmation": "",
|
||||
"Parent": "Üst Öğe",
|
||||
"Copy Link": "Bağlantıyı Kopyala",
|
||||
"Copy Token": "Anahtarı Kopyala",
|
||||
"delete_confirmation": "{source}'ı silmek istediğinizden emin misiniz?",
|
||||
"move_confirmation": "",
|
||||
"merge_confirmation": "",
|
||||
"create_rule": "",
|
||||
@@ -457,5 +457,41 @@
|
||||
"New_Supermarket": "Yeni Market",
|
||||
"New_Supermarket_Category": "Yeni Market Kategorisi",
|
||||
"Are_You_Sure": "Emin misin?",
|
||||
"Valid Until": "Geçerlilik Tarihi"
|
||||
"Valid Until": "Geçerlilik Tarihi",
|
||||
"err_importing_recipe": "Tarif içeri aktarılırken bir hata oluştu!",
|
||||
"Data_Import_Info": "Tarif koleksiyonunuzu geliştirmek için topluluk tarafından oluşturulmuş yiyecek, birim ve daha fazlasını olduğu listeleri içeri aktararak Alanlarınızı genişletin.",
|
||||
"open_data_help_text": "The Tandoor Open Data projesi Tandoor için topluluk tarafından oluşturulmuş verileri sağlar. Bu alan içeri aktarım sırasında otomatik olarak doldurulur ve gelecekte güncellenebilir.",
|
||||
"Description_Replace": "Açıklama Değiştir",
|
||||
"FDC_ID": "FDC Kimlik",
|
||||
"FDC_Search": "FDC Arama",
|
||||
"FDC_ID_help": "FDC veritabanı Kimlik",
|
||||
"Auto_Sort": "Otomatik Sırala",
|
||||
"Welcome": "Hoşgeldiniz",
|
||||
"recipe_property_info": "Yiyeceklere ayrıca özellikler ekleyebilir ve tarifinize göre bunları otomatik olarak hesaplayabilirsiniz!",
|
||||
"Amount": "Miktar",
|
||||
"per_serving": "servis başına",
|
||||
"Instruction_Replace": "Talimat Değiştir",
|
||||
"Open_Data_Slug": "Açık Veri Tanım",
|
||||
"Input": "Giriş",
|
||||
"Undo": "Geri Al",
|
||||
"NoMoreUndo": "Yapılacak değişiklik yok.",
|
||||
"Delete_All": "Tümünü sil",
|
||||
"Update_Existing_Data": "Mevcut Verileri Güncelleyin",
|
||||
"Use_Metric": "Metrik Birimler Kullan",
|
||||
"Learn_More": "Daha Fazla",
|
||||
"converted_unit": "Dönüştürülmüş Birim",
|
||||
"converted_amount": "Dönüştürülmüş Miktar",
|
||||
"base_unit": "Temel Birim",
|
||||
"Number of Objects": "Nesne Sayısı",
|
||||
"Datatype": "Veri tipi",
|
||||
"StartDate": "Başlangıç Tarihi",
|
||||
"EndDate": "Bitiş Tarihi",
|
||||
"Auto_Sort_Help": "Tüm malzemeleri en uygun adıma taşı.",
|
||||
"Open_Data_Import": "Açık Veri İçeri Aktar",
|
||||
"Properties_Food_Amount": "Özellikler Yiyecek Miktar",
|
||||
"Properties_Food_Unit": "Özellikler Yiyecek Birim",
|
||||
"base_amount": "Temel Miktar",
|
||||
"Calculator": "Hesap Makinesi",
|
||||
"property_type_fdc_hint": "Yalnızca FDC kimliği olan özellik tipleri FDC veritabanından veri çekebilir",
|
||||
"Alignment": "Hizalama"
|
||||
}
|
||||
|
||||
@@ -3717,25 +3717,7 @@ bn.js@^5.0.0, bn.js@^5.2.1:
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
|
||||
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
|
||||
|
||||
body-parser@1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.4"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
raw-body "2.5.1"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
body-parser@^1.19.0:
|
||||
body-parser@1.20.2, body-parser@^1.19.0:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
@@ -4511,10 +4493,10 @@ cookie-signature@1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
cookie@0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
||||
copy-concurrently@^1.0.0:
|
||||
version "1.0.5"
|
||||
@@ -5652,16 +5634,16 @@ express-history-api-fallback@^2.2.1:
|
||||
integrity sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg==
|
||||
|
||||
express@^4.17.1, express@^4.17.3:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.1"
|
||||
body-parser "1.20.2"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
@@ -9351,16 +9333,6 @@ range-parser@^1.2.1, range-parser@~1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
|
||||
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@2.5.2:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
|
||||
|
||||
Reference in New Issue
Block a user