diff --git a/.dockerignore b/.dockerignore index b14fea29a..a69161b0c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,7 +3,6 @@ npm-debug.log Dockerfile* docker-compose* .dockerignore -.git .gitignore README.md LICENSE diff --git a/.env.template b/.env.template index f0230072a..c1ba71f7b 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,10 @@ # when unset: 1 (true) - dont unset this, just for development DEBUG=0 SQL_DEBUG=0 +DEBUG_TOOLBAR=0 +# Gunicorn log level for debugging (default value is "info" when unset) +# (see https://docs.gunicorn.org/en/stable/settings.html#loglevel for available settings) +# GUNICORN_LOG_LEVEL="debug" # HTTP port to bind to # TANDOOR_PORT=8080 @@ -9,9 +13,18 @@ SQL_DEBUG=0 # hosts the application can run under e.g. recipes.mydomain.com,cooking.mydomain.com,... ALLOWED_HOSTS=* +# Cross Site Request Forgery protection +# (https://docs.djangoproject.com/en/4.2/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS) +# CSRF_TRUSTED_ORIGINS = [] + +# Cross Origin Resource Sharing +# (https://github.com/adamchainz/django-cors-header) +# CORS_ALLOW_ALL_ORIGINS = True + # random secret key, use for example `base64 /dev/urandom | head -c50` to generate one -# ---------------------------- REQUIRED ------------------------- +# ---------------------------- AT LEAST ONE REQUIRED ------------------------- SECRET_KEY= +SECRET_KEY_FILE= # --------------------------------------------------------------- # your default timezone See https://timezonedb.com/time-zones for a list of timezones @@ -23,8 +36,9 @@ DB_ENGINE=django.db.backends.postgresql POSTGRES_HOST=db_recipes POSTGRES_PORT=5432 POSTGRES_USER=djangouser -# ---------------------------- REQUIRED ------------------------- +# ---------------------------- AT LEAST ONE REQUIRED ------------------------- POSTGRES_PASSWORD= +POSTGRES_PASSWORD_FILE= # --------------------------------------------------------------- POSTGRES_DB=djangodb @@ -96,10 +110,12 @@ GUNICORN_MEDIA=0 # prefix used for account related emails (default "[Tandoor Recipes] ") # ACCOUNT_EMAIL_SUBJECT_PREFIX= -# allow authentication via reverse proxy (e.g. authelia), leave off if you dont know what you are doing -# see docs for more information https://docs.tandoor.dev/features/authentication/ +# allow authentication via the REMOTE-USER header (can be used for e.g. authelia). +# ATTENTION: Leave off if you don't know what you are doing! Enabling this without proper configuration will enable anybody +# to login with any username! +# See docs for additional information: https://docs.tandoor.dev/features/authentication/#reverse-proxy-authentication # when unset: 0 (false) -REVERSE_PROXY_AUTH=0 +REMOTE_USER_AUTH=0 # Default settings for spaces, apply per space and can be changed in the admin view # SPACE_DEFAULT_MAX_RECIPES=0 # 0=unlimited recipes @@ -107,7 +123,8 @@ REVERSE_PROXY_AUTH=0 # SPACE_DEFAULT_MAX_FILES=0 # Maximum file storage for space in MB. 0 for unlimited, -1 to disable file upload. # SPACE_DEFAULT_ALLOW_SHARING=1 # Allow users to share recipes with public links -# allow people to create accounts on your application instance (without an invite link) +# allow people to create local accounts on your application instance (without an invite link) +# social accounts will always be able to sign up # when unset: 0 (false) # ENABLE_SIGNUP=0 @@ -157,6 +174,7 @@ REVERSE_PROXY_AUTH=0 #AUTH_LDAP_BIND_PASSWORD= #AUTH_LDAP_USER_SEARCH_BASE_DN= #AUTH_LDAP_TLS_CACERTFILE= +#AUTH_LDAP_START_TLS= # Enables exporting PDF (see export docs) # Disabled by default, uncomment to enable diff --git a/.github/workflows/build-docker-open-data.yml b/.github/workflows/build-docker-open-data.yml new file mode 100644 index 000000000..881e7824b --- /dev/null +++ b/.github/workflows/build-docker-open-data.yml @@ -0,0 +1,110 @@ +name: Build Docker Container with open data plugin installed + +on: push + +jobs: + build-container: + name: Build ${{ matrix.name }} Container + runs-on: ubuntu-latest + if: github.repository_owner == 'TandoorRecipes' + continue-on-error: ${{ matrix.continue-on-error }} + permissions: + contents: read + packages: write + strategy: + matrix: + include: + # Standard build config + - name: Standard + dockerfile: Dockerfile + platforms: linux/amd64,linux/arm64 + suffix: "" + continue-on-error: false + steps: + - uses: actions/checkout@v3 + + - name: Get version number + id: get_version + run: | + if [[ "$GITHUB_REF" = refs/tags/* ]]; then + echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT + elif [[ "$GITHUB_REF" = refs/heads/beta ]]; then + echo VERSION=beta >> $GITHUB_OUTPUT + else + echo VERSION=develop >> $GITHUB_OUTPUT + fi + + # clone open data plugin + - name: clone open data plugin repo + uses: actions/checkout@master + with: + repository: TandoorRecipes/open_data_plugin + ref: master + path: ./recipes/plugins/open_data_plugin + + # Build Vue frontend + - uses: actions/setup-node@v3 + with: + node-version: '18' + cache: yarn + cache-dependency-path: vue/yarn.lock + - name: Install dependencies + working-directory: ./vue + run: yarn install --frozen-lockfile + - name: Build dependencies + working-directory: ./vue + run: yarn build + + - name: Setup Open Data Plugin Links + working-directory: ./recipes/plugins/open_data_plugin + run: python setup_repo.py + + - name: Build Open Data Frontend + working-directory: ./recipes/plugins/open_data_plugin/vue + run: yarn build + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 + if: github.secret_source == 'Actions' + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + if: github.secret_source == 'Actions' + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + vabene1111/recipes + ghcr.io/TandoorRecipes/recipes + flavor: | + latest=false + suffix=${{ matrix.suffix }} + tags: | + type=raw,value=latest,suffix=-open-data-plugin,enable=${{ startsWith(github.ref, 'refs/tags/') }} + type=semver,suffix=-open-data-plugin,pattern={{version}} + type=semver,suffix=-open-data-plugin,pattern={{major}}.{{minor}} + type=semver,suffix=-open-data-plugin,pattern={{major}} + type=ref,suffix=-open-data-plugin,event=branch + - name: Build and Push + uses: docker/build-push-action@v4 + with: + context: . + file: ${{ matrix.dockerfile }} + pull: true + push: ${{ github.secret_source == 'Actions' }} + platforms: ${{ matrix.platforms }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 072c86fa4..aea8a5265 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -17,15 +17,9 @@ jobs: # Standard build config - name: Standard dockerfile: Dockerfile - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64,linux/arm64,linux/arm/v7 suffix: "" continue-on-error: false - # Raspi build config - - name: Raspi - dockerfile: Dockerfile-raspi - platforms: linux/arm/v7 - suffix: "-raspi" - continue-on-error: true steps: - uses: actions/checkout@v3 @@ -40,20 +34,10 @@ jobs: echo VERSION=develop >> $GITHUB_OUTPUT fi - # Update Version number - - name: Update version file - uses: DamianReeves/write-file-action@v1.2 - with: - path: recipes/version.py - contents: | - VERSION_NUMBER = '${{ steps.get_version.outputs.VERSION }}' - BUILD_REF = '${{ github.sha }}' - write-mode: overwrite - # Build Vue frontend - uses: actions/setup-node@v3 with: - node-version: '14' + node-version: '18' cache: yarn cache-dependency-path: vue/yarn.lock - name: Install dependencies @@ -115,13 +99,17 @@ jobs: needs: build-container if: startsWith(github.ref, 'refs/tags/') steps: + - name: Set tag name + run: | + # Strip "refs/tags/" prefix + echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV # Send stable discord notification - name: Discord notification env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} uses: Ilshidur/action-discord@0.3.2 with: - args: '🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of tandoor has been released 🥳 Check it out https://github.com/vabene1111/recipes/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}' + args: '🚀 Version {{ VERSION }} of tandoor has been released 🥳 Check it out https://github.com/vabene1111/recipes/releases/tag/{{ VERSION }}' notify-beta: name: Notify Beta diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8492e717d..d699a5641 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: # Build Vue frontend - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' - name: Install Vue dependencies working-directory: ./vue run: yarn install diff --git a/.gitignore b/.gitignore index b0d71b268..ad9c1fc7f 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ postgresql/ /docker-compose.override.yml vue/node_modules +plugins .vscode/ vetur.config.js cookbook/static/vue diff --git a/.idea/recipes.iml b/.idea/recipes.iml index 1b96c9d80..8a0e59c8e 100644 --- a/.idea/recipes.iml +++ b/.idea/recipes.iml @@ -18,7 +18,7 @@ - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index befb1f6af..e8471d171 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM python:3.10-alpine3.15 +FROM python:3.10-alpine3.18 #Install all dependencies. -RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev py-cryptography openldap +RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git #Print all logs without buffering it. ENV PYTHONUNBUFFERED 1 @@ -15,7 +15,11 @@ WORKDIR /opt/recipes COPY 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 git && \ +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 && \ python -m venv venv && \ /opt/recipes/venv/bin/python -m pip install --upgrade pip && \ @@ -26,5 +30,11 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de #Copy project and execute it. COPY . ./ + +# collect information from git repositories +RUN /opt/recipes/venv/bin/python version.py +# delete git repositories to reduce image size +RUN find . -type d -name ".git" | xargs rm -rf + RUN chmod +x boot.sh ENTRYPOINT ["/opt/recipes/boot.sh"] diff --git a/Dockerfile-raspi b/Dockerfile-raspi deleted file mode 100644 index 7640d52da..000000000 --- a/Dockerfile-raspi +++ /dev/null @@ -1,33 +0,0 @@ -# builds of cryptography for raspberry pi (or better arm v7) fail for some -FROM python:3.9-alpine3.15 - -#Install all dependencies. -RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev py-cryptography openldap gcompat - -#Print all logs without buffering it. -ENV PYTHONUNBUFFERED 1 - -#This port will be used by gunicorn. -EXPOSE 8080 - -#Create app dir and install requirements. -RUN mkdir /opt/recipes -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 -RUN apk add --no-cache --virtual .build-deps gcc musl-dev zlib-dev jpeg-dev libwebp-dev python3-dev git && \ - echo -n "INPUT ( libldap.so )" > /usr/lib/libldap_r.so && \ - python -m venv venv && \ - /opt/recipes/venv/bin/python -m pip install --upgrade pip && \ - venv/bin/pip install wheel==0.37.1 && \ - venv/bin/pip install -r requirements.txt --no-cache-dir --no-binary=Pillow && \ - apk --purge del .build-deps - -#Copy project and execute it. -COPY . ./ -RUN chmod +x boot.sh -ENTRYPOINT ["/opt/recipes/boot.sh"] diff --git a/boot.sh b/boot.sh index b63217411..2e78ba93e 100644 --- a/boot.sh +++ b/boot.sh @@ -4,6 +4,7 @@ source venv/bin/activate TANDOOR_PORT="${TANDOOR_PORT:-8080}" GUNICORN_WORKERS="${GUNICORN_WORKERS:-3}" GUNICORN_THREADS="${GUNICORN_THREADS:-2}" +GUNICORN_LOG_LEVEL="${GUNICORN_LOG_LEVEL:-'info'}" NGINX_CONF_FILE=/opt/recipes/nginx/conf.d/Recipes.conf display_warning() { @@ -18,9 +19,14 @@ if [ ! -f "$NGINX_CONF_FILE" ] && [ $GUNICORN_MEDIA -eq 0 ]; then display_warning "Nginx configuration file could not be found at the default location!\nPath: ${NGINX_CONF_FILE}" fi -# SECRET_KEY must be set in .env file +# SECRET_KEY (or a valid file at SECRET_KEY_FILE) must be set in .env file + +if [ -f "${SECRET_KEY_FILE}" ]; then + export SECRET_KEY=$(cat "$SECRET_KEY_FILE") +fi + if [ -z "${SECRET_KEY}" ]; then - display_warning "The environment variable 'SECRET_KEY' is not set but REQUIRED for running Tandoor!" + display_warning "The environment variable 'SECRET_KEY' (or 'SECRET_KEY_FILE' that points to an existing file) is not set but REQUIRED for running Tandoor!" fi @@ -31,9 +37,14 @@ max_attempts=20 if [ "${DB_ENGINE}" != 'django.db.backends.sqlite3' ]; then - # POSTGRES_PASSWORD must be set in .env file + # POSTGRES_PASSWORD (or a valid file at POSTGRES_PASSWORD_FILE) must be set in .env file + + if [ -f "${POSTGRES_PASSWORD_FILE}" ]; then + export POSTGRES_PASSWORD=$(cat "$POSTGRES_PASSWORD_FILE") + fi + if [ -z "${POSTGRES_PASSWORD}" ]; then - display_warning "The environment variable 'POSTGRES_PASSWORD' is not set but REQUIRED for running Tandoor!" + display_warning "The environment variable 'POSTGRES_PASSWORD' (or 'POSTGRES_PASSWORD_FILE' that points to an existing file) is not set but REQUIRED for running Tandoor!" fi while pg_isready --host=${POSTGRES_HOST} --port=${POSTGRES_PORT} --user=${POSTGRES_USER} -q; status=$?; attempt=$((attempt+1)); [ $status -ne 0 ] && [ $attempt -le $max_attempts ]; do @@ -65,4 +76,4 @@ echo "Done" chmod -R 755 /opt/recipes/mediafiles -exec gunicorn -b :$TANDOOR_PORT --workers $GUNICORN_WORKERS --threads $GUNICORN_THREADS --access-logfile - --error-logfile - --log-level INFO recipes.wsgi +exec gunicorn -b :$TANDOOR_PORT --workers $GUNICORN_WORKERS --threads $GUNICORN_THREADS --access-logfile - --error-logfile - --log-level $GUNICORN_LOG_LEVEL recipes.wsgi diff --git a/cookbook/admin.py b/cookbook/admin.py index aba876dfb..b47afd92e 100644 --- a/cookbook/admin.py +++ b/cookbook/admin.py @@ -10,12 +10,13 @@ from treebeard.forms import movenodeform_factory from cookbook.managers import DICTIONARY -from .models import (BookmarkletImport, Comment, CookLog, Food, FoodInheritField, ImportLog, - Ingredient, InviteLink, Keyword, MealPlan, MealType, NutritionInformation, +from .models import (BookmarkletImport, Comment, CookLog, Food, ImportLog, Ingredient, InviteLink, + Keyword, MealPlan, MealType, NutritionInformation, Property, PropertyType, Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage, Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, - TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace) + TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace, + ViewLog) class CustomUserAdmin(UserAdmin): @@ -38,6 +39,8 @@ def delete_space_action(modeladmin, request, queryset): class SpaceAdmin(admin.ModelAdmin): list_display = ('name', 'created_by', 'max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing') search_fields = ('name', 'created_by__username') + autocomplete_fields = ('created_by',) + filter_horizontal = ('food_inherit',) list_filter = ('max_recipes', 'max_users', 'max_file_storage_mb', 'allow_sharing') date_hierarchy = 'created_at' actions = [delete_space_action] @@ -49,6 +52,8 @@ admin.site.register(Space, SpaceAdmin) class UserSpaceAdmin(admin.ModelAdmin): list_display = ('user', 'space',) search_fields = ('user__username', 'space__name',) + filter_horizontal = ('groups',) + autocomplete_fields = ('user', 'space',) admin.site.register(UserSpace, UserSpaceAdmin) @@ -59,6 +64,7 @@ class UserPreferenceAdmin(admin.ModelAdmin): search_fields = ('user__username',) list_filter = ('theme', 'nav_color', 'default_page',) date_hierarchy = 'created_at' + filter_horizontal = ('plan_share', 'shopping_share',) @staticmethod def name(obj): @@ -150,9 +156,16 @@ class KeywordAdmin(TreeAdmin): admin.site.register(Keyword, KeywordAdmin) +@admin.action(description='Delete Steps not part of a Recipe.') +def delete_unattached_steps(modeladmin, request, queryset): + with scopes_disabled(): + Step.objects.filter(recipe=None).delete() + + class StepAdmin(admin.ModelAdmin): list_display = ('name', 'order',) search_fields = ('name',) + actions = [delete_unattached_steps] admin.site.register(Step, StepAdmin) @@ -201,9 +214,24 @@ class FoodAdmin(TreeAdmin): admin.site.register(Food, FoodAdmin) +class UnitConversionAdmin(admin.ModelAdmin): + list_display = ('base_amount', 'base_unit', 'food', 'converted_amount', 'converted_unit') + search_fields = ('food__name', 'unit__name') + + +admin.site.register(UnitConversion, UnitConversionAdmin) + + +@admin.action(description='Delete Ingredients not part of a Recipe.') +def delete_unattached_ingredients(modeladmin, request, queryset): + with scopes_disabled(): + Ingredient.objects.filter(step__recipe=None).delete() + + class IngredientAdmin(admin.ModelAdmin): list_display = ('food', 'amount', 'unit') search_fields = ('food__name', 'unit__name') + actions = [delete_unattached_ingredients] admin.site.register(Ingredient, IngredientAdmin) @@ -249,7 +277,7 @@ admin.site.register(RecipeBookEntry, RecipeBookEntryAdmin) class MealPlanAdmin(admin.ModelAdmin): - list_display = ('user', 'recipe', 'meal_type', 'date') + list_display = ('user', 'recipe', 'meal_type', 'from_date', 'to_date') @staticmethod def user(obj): @@ -286,6 +314,7 @@ admin.site.register(InviteLink, InviteLinkAdmin) class CookLogAdmin(admin.ModelAdmin): list_display = ('recipe', 'created_by', 'created_at', 'rating', 'servings') + search_fields = ('recipe__name', 'space__name',) admin.site.register(CookLog, CookLogAdmin) @@ -319,6 +348,20 @@ class ShareLinkAdmin(admin.ModelAdmin): admin.site.register(ShareLink, ShareLinkAdmin) +class PropertyTypeAdmin(admin.ModelAdmin): + list_display = ('id', 'name') + + +admin.site.register(PropertyType, PropertyTypeAdmin) + + +class PropertyAdmin(admin.ModelAdmin): + list_display = ('property_amount', 'property_type') + + +admin.site.register(Property, PropertyAdmin) + + class NutritionInformationAdmin(admin.ModelAdmin): list_display = ('id',) diff --git a/cookbook/forms.py b/cookbook/forms.py index 1c1a90c0e..fb557dd94 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -45,7 +45,7 @@ class UserPreferenceForm(forms.ModelForm): model = UserPreference fields = ( 'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color', - 'sticky_navbar', 'default_page', 'plan_share', 'ingredient_decimals', 'comments', 'left_handed', + 'sticky_navbar', 'default_page', 'plan_share', 'ingredient_decimals', 'comments', 'left_handed', 'show_step_ingredients', ) labels = { @@ -60,29 +60,29 @@ class UserPreferenceForm(forms.ModelForm): 'ingredient_decimals': _('Ingredient decimal places'), 'shopping_auto_sync': _('Shopping list auto sync period'), 'comments': _('Comments'), - 'left_handed': _('Left-handed mode') + 'left_handed': _('Left-handed mode'), + 'show_step_ingredients': _('Show step ingredients table') } help_texts = { 'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'), - - 'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'), + 'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'), 'use_fractions': _( 'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'), - - 'use_kj': _('Display nutritional energy amounts in joules instead of calories'), + 'use_kj': _('Display nutritional energy amounts in joules instead of calories'), 'plan_share': _('Users with whom newly created meal plans should be shared by default.'), 'shopping_share': _('Users with whom to share shopping lists.'), - 'ingredient_decimals': _('Number of decimals to round ingredients.'), - 'comments': _('If you want to be able to create and see comments underneath recipes.'), + 'ingredient_decimals': _('Number of decimals to round ingredients.'), + 'comments': _('If you want to be able to create and see comments underneath recipes.'), 'shopping_auto_sync': _( - 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' - 'of mobile data. If lower than instance limit it is reset when saving.' + 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' + 'of mobile data. If lower than instance limit it is reset when saving.' ), - 'sticky_navbar': _('Makes the navbar stick to the top of the page.'), + 'sticky_navbar': _('Makes the navbar stick to the top of the page.'), 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'), 'mealplan_autoexclude_onhand': _('Exclude ingredients that are on hand.'), - 'left_handed': _('Will optimize the UI for use with your left hand.') + 'left_handed': _('Will optimize the UI for use with your left hand.'), + 'show_step_ingredients': _('Add ingredients table next to recipe steps. Applies at creation time for manually created and URL imported recipes. Individual steps can be overridden in the edit recipe view.') } widgets = { @@ -167,8 +167,26 @@ class ImportExportBase(forms.Form): )) +class MultipleFileInput(forms.ClearableFileInput): + allow_multiple_selected = True + + +class MultipleFileField(forms.FileField): + def __init__(self, *args, **kwargs): + kwargs.setdefault("widget", MultipleFileInput()) + super().__init__(*args, **kwargs) + + def clean(self, data, initial=None): + single_file_clean = super().clean + if isinstance(data, (list, tuple)): + result = [single_file_clean(d, initial) for d in data] + else: + result = single_file_clean(data, initial) + return result + + class ImportForm(ImportExportBase): - files = forms.FileField(required=True, widget=forms.ClearableFileInput(attrs={'multiple': True})) + files = MultipleFileField(required=True) duplicates = forms.BooleanField(help_text=_( 'To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'), required=False) @@ -305,50 +323,6 @@ class ImportRecipeForm(forms.ModelForm): } -# TODO deprecate -class MealPlanForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - space = kwargs.pop('space') - super().__init__(*args, **kwargs) - self.fields['recipe'].queryset = Recipe.objects.filter(space=space).all() - self.fields['meal_type'].queryset = MealType.objects.filter(space=space).all() - self.fields['shared'].queryset = User.objects.filter(userpreference__space=space).all() - - def clean(self): - cleaned_data = super(MealPlanForm, self).clean() - - if cleaned_data['title'] == '' and cleaned_data['recipe'] is None: - raise forms.ValidationError( - _('You must provide at least a recipe or a title.') - ) - - return cleaned_data - - class Meta: - model = MealPlan - fields = ( - 'recipe', 'title', 'meal_type', 'note', - 'servings', 'date', 'shared' - ) - - help_texts = { - 'shared': _('You can list default users to share recipes with in the settings.'), - 'note': _('You can use markdown to format this field. See the docs here') - - } - - widgets = { - 'recipe': SelectWidget, - 'date': DateWidget, - 'shared': MultiSelectWidget - } - field_classes = { - 'recipe': SafeModelChoiceField, - 'meal_type': SafeModelChoiceField, - 'shared': SafeModelMultipleChoiceField, - } - - class InviteLinkForm(forms.ModelForm): def __init__(self, *args, **kwargs): user = kwargs.pop('user') @@ -489,8 +463,8 @@ class ShoppingPreferenceForm(forms.ModelForm): help_texts = { 'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'), 'shopping_auto_sync': _( - 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' - 'of mobile data. If lower than instance limit it is reset when saving.' + 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit ' + 'of mobile data. If lower than instance limit it is reset when saving.' ), 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'), 'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'), @@ -534,11 +508,10 @@ class SpacePreferenceForm(forms.ModelForm): class Meta: model = Space - fields = ('food_inherit', 'reset_food_inherit', 'show_facet_count', 'use_plural') + fields = ('food_inherit', 'reset_food_inherit', 'use_plural') help_texts = { 'food_inherit': _('Fields on food that should be inherited by default.'), - 'show_facet_count': _('Show recipe counts on search filters'), 'use_plural': _('Use the plural form for units and food inside this space.'), } diff --git a/cookbook/helper/AllAuthCustomAdapter.py b/cookbook/helper/AllAuthCustomAdapter.py index 4975251a4..a1affa4ce 100644 --- a/cookbook/helper/AllAuthCustomAdapter.py +++ b/cookbook/helper/AllAuthCustomAdapter.py @@ -1,11 +1,10 @@ import datetime - -from django.conf import settings +from gettext import gettext as _ from allauth.account.adapter import DefaultAccountAdapter +from django.conf import settings from django.contrib import messages from django.core.cache import caches -from gettext import gettext as _ from cookbook.models import InviteLink @@ -17,10 +16,13 @@ class AllAuthCustomAdapter(DefaultAccountAdapter): Whether to allow sign-ups. """ signup_token = False - if 'signup_token' in request.session and InviteLink.objects.filter(valid_until__gte=datetime.datetime.today(), used_by=None, uuid=request.session['signup_token']).exists(): + if 'signup_token' in request.session and InviteLink.objects.filter( + valid_until__gte=datetime.datetime.today(), used_by=None, uuid=request.session['signup_token']).exists(): signup_token = True - if (request.resolver_match.view_name == 'account_signup' or request.resolver_match.view_name == 'socialaccount_signup') and not settings.ENABLE_SIGNUP and not signup_token: + if request.resolver_match.view_name == 'account_signup' and not settings.ENABLE_SIGNUP and not signup_token: + return False + elif request.resolver_match.view_name == 'socialaccount_signup' and len(settings.SOCIAL_PROVIDERS) < 1: return False else: return super(AllAuthCustomAdapter, self).is_open_for_signup(request) @@ -33,7 +35,7 @@ class AllAuthCustomAdapter(DefaultAccountAdapter): if c == default: try: super(AllAuthCustomAdapter, self).send_mail(template_prefix, email, context) - except Exception: # dont fail signup just because confirmation mail could not be send + except Exception: # dont fail signup just because confirmation mail could not be send pass else: messages.add_message(self.request, messages.ERROR, _('In order to prevent spam, the requested email was not send. Please wait a few minutes and try again.')) diff --git a/cookbook/helper/__init__.py b/cookbook/helper/__init__.py index c1cb37885..e4fea45ad 100644 --- a/cookbook/helper/__init__.py +++ b/cookbook/helper/__init__.py @@ -1,6 +1,3 @@ -import cookbook.helper.dal -from cookbook.helper.AllAuthCustomAdapter import AllAuthCustomAdapter - __all__ = [ 'dal', ] diff --git a/cookbook/helper/automation_helper.py b/cookbook/helper/automation_helper.py new file mode 100644 index 000000000..a86d405bd --- /dev/null +++ b/cookbook/helper/automation_helper.py @@ -0,0 +1,227 @@ +import re + +from django.core.cache import caches +from django.db.models.functions import Lower + +from cookbook.models import Automation + + +class AutomationEngine: + request = None + source = None + use_cache = None + food_aliases = None + keyword_aliases = None + unit_aliases = None + never_unit = None + transpose_words = None + regex_replace = { + Automation.DESCRIPTION_REPLACE: None, + Automation.INSTRUCTION_REPLACE: None, + Automation.FOOD_REPLACE: None, + Automation.UNIT_REPLACE: None, + Automation.NAME_REPLACE: None, + } + + def __init__(self, request, use_cache=True, source=None): + self.request = request + self.use_cache = use_cache + if not source: + self.source = "default_string_to_avoid_false_regex_match" + else: + self.source = source + + def apply_keyword_automation(self, keyword): + keyword = keyword.strip() + if self.use_cache and self.keyword_aliases is None: + self.keyword_aliases = {} + KEYWORD_CACHE_KEY = f'automation_keyword_alias_{self.request.space.pk}' + if c := caches['default'].get(KEYWORD_CACHE_KEY, None): + self.keyword_aliases = c + caches['default'].touch(KEYWORD_CACHE_KEY, 30) + else: + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.KEYWORD_ALIAS).only('param_1', 'param_2').order_by('order').all(): + self.keyword_aliases[a.param_1.lower()] = a.param_2 + caches['default'].set(KEYWORD_CACHE_KEY, self.keyword_aliases, 30) + else: + self.keyword_aliases = {} + if self.keyword_aliases: + try: + keyword = self.keyword_aliases[keyword.lower()] + except KeyError: + pass + else: + if automation := Automation.objects.filter(space=self.request.space, type=Automation.KEYWORD_ALIAS, param_1__iexact=keyword, disabled=False).order_by('order').first(): + return automation.param_2 + return keyword + + def apply_unit_automation(self, unit): + unit = unit.strip() + if self.use_cache and self.unit_aliases is None: + self.unit_aliases = {} + UNIT_CACHE_KEY = f'automation_unit_alias_{self.request.space.pk}' + if c := caches['default'].get(UNIT_CACHE_KEY, None): + self.unit_aliases = c + caches['default'].touch(UNIT_CACHE_KEY, 30) + else: + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').order_by('order').all(): + self.unit_aliases[a.param_1.lower()] = a.param_2 + caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30) + else: + self.unit_aliases = {} + if self.unit_aliases: + try: + unit = self.unit_aliases[unit.lower()] + except KeyError: + pass + else: + if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1__iexact=unit, disabled=False).order_by('order').first(): + return automation.param_2 + return self.apply_regex_replace_automation(unit, Automation.UNIT_REPLACE) + + def apply_food_automation(self, food): + food = food.strip() + if self.use_cache and self.food_aliases is None: + self.food_aliases = {} + FOOD_CACHE_KEY = f'automation_food_alias_{self.request.space.pk}' + if c := caches['default'].get(FOOD_CACHE_KEY, None): + self.food_aliases = c + caches['default'].touch(FOOD_CACHE_KEY, 30) + else: + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all(): + self.food_aliases[a.param_1.lower()] = a.param_2 + caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30) + else: + self.food_aliases = {} + + if self.food_aliases: + try: + return self.food_aliases[food.lower()] + except KeyError: + return food + else: + if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1__iexact=food, disabled=False).order_by('order').first(): + return automation.param_2 + return self.apply_regex_replace_automation(food, Automation.FOOD_REPLACE) + + def apply_never_unit_automation(self, tokens): + """ + Moves a string that should never be treated as a unit to next token and optionally replaced with default unit + e.g. NEVER_UNIT: param1: egg, param2: None would modify ['1', 'egg', 'white'] to ['1', '', 'egg', 'white'] + or NEVER_UNIT: param1: egg, param2: pcs would modify ['1', 'egg', 'yolk'] to ['1', 'pcs', 'egg', 'yolk'] + :param1 string: string that should never be considered a unit, will be moved to token[2] + :param2 (optional) unit as string: will insert unit string into token[1] + :return: unit as string (possibly changed by automation) + """ + + if self.use_cache and self.never_unit is None: + self.never_unit = {} + NEVER_UNIT_CACHE_KEY = f'automation_never_unit_{self.request.space.pk}' + if c := caches['default'].get(NEVER_UNIT_CACHE_KEY, None): + self.never_unit = c + caches['default'].touch(NEVER_UNIT_CACHE_KEY, 30) + else: + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.NEVER_UNIT).only('param_1', 'param_2').order_by('order').all(): + self.never_unit[a.param_1.lower()] = a.param_2 + caches['default'].set(NEVER_UNIT_CACHE_KEY, self.never_unit, 30) + else: + self.never_unit = {} + + new_unit = None + alt_unit = self.apply_unit_automation(tokens[1]) + never_unit = False + if self.never_unit: + try: + new_unit = self.never_unit[tokens[1].lower()] + never_unit = True + except KeyError: + return tokens + else: + if a := Automation.objects.annotate(param_1_lower=Lower('param_1')).filter(space=self.request.space, type=Automation.NEVER_UNIT, param_1_lower__in=[ + tokens[1].lower(), alt_unit.lower()], disabled=False).order_by('order').first(): + new_unit = a.param_2 + never_unit = True + + if never_unit: + tokens.insert(1, new_unit) + return tokens + + def apply_transpose_automation(self, string): + """ + If two words (param_1 & param_2) are detected in sequence, swap their position in the ingredient string + :param 1: first word to detect + :param 2: second word to detect + return: new ingredient string + """ + if self.use_cache and self.transpose_words is None: + self.transpose_words = {} + TRANSPOSE_WORDS_CACHE_KEY = f'automation_transpose_words_{self.request.space.pk}' + if c := caches['default'].get(TRANSPOSE_WORDS_CACHE_KEY, None): + self.transpose_words = c + caches['default'].touch(TRANSPOSE_WORDS_CACHE_KEY, 30) + else: + i = 0 + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.TRANSPOSE_WORDS).only( + 'param_1', 'param_2').order_by('order').all()[:512]: + self.transpose_words[i] = [a.param_1.lower(), a.param_2.lower()] + i += 1 + caches['default'].set(TRANSPOSE_WORDS_CACHE_KEY, self.transpose_words, 30) + else: + self.transpose_words = {} + + tokens = [x.lower() for x in string.replace(',', ' ').split()] + if self.transpose_words: + for key, value in self.transpose_words.items(): + if value[0] in tokens and value[1] in tokens: + string = re.sub(rf"\b({value[0]})\W*({value[1]})\b", r"\2 \1", string, flags=re.IGNORECASE) + else: + for rule in Automation.objects.filter(space=self.request.space, type=Automation.TRANSPOSE_WORDS, disabled=False) \ + .annotate(param_1_lower=Lower('param_1'), param_2_lower=Lower('param_2')) \ + .filter(param_1_lower__in=tokens, param_2_lower__in=tokens).order_by('order')[:512]: + if rule.param_1 in tokens and rule.param_2 in tokens: + string = re.sub(rf"\b({rule.param_1})\W*({rule.param_2})\b", r"\2 \1", string, flags=re.IGNORECASE) + return string + + def apply_regex_replace_automation(self, string, automation_type): + # TODO add warning - maybe on SPACE page? when a max of 512 automations of a specific type is exceeded (ALIAS types excluded?) + """ + Replaces strings in a recipe field that are from a matched source + field_type are Automation.type that apply regex replacements + Automation.DESCRIPTION_REPLACE + Automation.INSTRUCTION_REPLACE + Automation.FOOD_REPLACE + Automation.UNIT_REPLACE + Automation.NAME_REPLACE + + regex replacment utilized the following fields from the Automation model + :param 1: source that should apply the automation in regex format ('.*' for all) + :param 2: regex pattern to match () + :param 3: replacement string (leave blank to delete) + return: new string + """ + if self.use_cache and self.regex_replace[automation_type] is None: + self.regex_replace[automation_type] = {} + REGEX_REPLACE_CACHE_KEY = f'automation_regex_replace_{self.request.space.pk}' + if c := caches['default'].get(REGEX_REPLACE_CACHE_KEY, None): + self.regex_replace[automation_type] = c[automation_type] + caches['default'].touch(REGEX_REPLACE_CACHE_KEY, 30) + else: + i = 0 + for a in Automation.objects.filter(space=self.request.space, disabled=False, type=automation_type).only( + 'param_1', 'param_2', 'param_3').order_by('order').all()[:512]: + self.regex_replace[automation_type][i] = [a.param_1, a.param_2, a.param_3] + i += 1 + caches['default'].set(REGEX_REPLACE_CACHE_KEY, self.regex_replace, 30) + else: + self.regex_replace[automation_type] = {} + + if self.regex_replace[automation_type]: + for rule in self.regex_replace[automation_type].values(): + if re.match(rule[0], (self.source)[:512]): + string = re.sub(rule[1], rule[2], string, flags=re.IGNORECASE) + else: + for rule in Automation.objects.filter(space=self.request.space, disabled=False, type=automation_type).only( + 'param_1', 'param_2', 'param_3').order_by('order').all()[:512]: + if re.match(rule.param_1, (self.source)[:512]): + string = re.sub(rule.param_2, rule.param_3, string, flags=re.IGNORECASE) + return string diff --git a/cookbook/helper/cache_helper.py b/cookbook/helper/cache_helper.py new file mode 100644 index 000000000..da903c53b --- /dev/null +++ b/cookbook/helper/cache_helper.py @@ -0,0 +1,11 @@ +class CacheHelper: + space = None + + BASE_UNITS_CACHE_KEY = None + PROPERTY_TYPE_CACHE_KEY = None + + def __init__(self, space): + self.space = space + + self.BASE_UNITS_CACHE_KEY = f'SPACE_{space.id}_BASE_UNITS' + self.PROPERTY_TYPE_CACHE_KEY = f'SPACE_{space.id}_PROPERTY_TYPES' diff --git a/cookbook/helper/image_processing.py b/cookbook/helper/image_processing.py index 552bf1312..5fdada0c4 100644 --- a/cookbook/helper/image_processing.py +++ b/cookbook/helper/image_processing.py @@ -1,8 +1,7 @@ import os -import sys +from io import BytesIO from PIL import Image -from io import BytesIO def rescale_image_jpeg(image_object, base_width=1020): @@ -40,7 +39,12 @@ def get_filetype(name): # TODO also add env variable to define which images sizes should be compressed # filetype argument can not be optional, otherwise this function will treat all images as if they were a jpeg # Because it's no longer optional, no reason to return it -def handle_image(request, image_object, filetype): +def handle_image(request, image_object, filetype): + try: + Image.open(image_object).verify() + except Exception: + return None + if (image_object.size / 1000) > 500: # if larger than 500 kb compress if filetype == '.jpeg' or filetype == '.jpg': return rescale_image_jpeg(image_object) diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py index 8ecf299b9..f944e4164 100644 --- a/cookbook/helper/ingredient_parser.py +++ b/cookbook/helper/ingredient_parser.py @@ -2,18 +2,16 @@ import re import string import unicodedata -from django.core.cache import caches - -from cookbook.models import Unit, Food, Automation, Ingredient +from cookbook.helper.automation_helper import AutomationEngine +from cookbook.models import Food, Ingredient, Unit class IngredientParser: request = None ignore_rules = False - food_aliases = {} - unit_aliases = {} + automation = None - def __init__(self, request, cache_mode, ignore_automations=False): + def __init__(self, request, cache_mode=True, ignore_automations=False): """ Initialize ingredient parser :param request: request context (to control caching, rule ownership, etc.) @@ -22,65 +20,8 @@ class IngredientParser: """ self.request = request self.ignore_rules = ignore_automations - if cache_mode: - FOOD_CACHE_KEY = f'automation_food_alias_{self.request.space.pk}' - if c := caches['default'].get(FOOD_CACHE_KEY, None): - self.food_aliases = c - caches['default'].touch(FOOD_CACHE_KEY, 30) - else: - for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all(): - self.food_aliases[a.param_1] = a.param_2 - caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30) - - UNIT_CACHE_KEY = f'automation_unit_alias_{self.request.space.pk}' - if c := caches['default'].get(UNIT_CACHE_KEY, None): - self.unit_aliases = c - caches['default'].touch(UNIT_CACHE_KEY, 30) - else: - for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').order_by('order').all(): - self.unit_aliases[a.param_1] = a.param_2 - caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30) - else: - self.food_aliases = {} - self.unit_aliases = {} - - def apply_food_automation(self, food): - """ - Apply food alias automations to passed food - :param food: unit as string - :return: food as string (possibly changed by automation) - """ - if self.ignore_rules: - return food - else: - if self.food_aliases: - try: - return self.food_aliases[food] - except KeyError: - return food - else: - if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1=food, disabled=False).order_by('order').first(): - return automation.param_2 - return food - - def apply_unit_automation(self, unit): - """ - Apply unit alias automations to passed unit - :param unit: unit as string - :return: unit as string (possibly changed by automation) - """ - if self.ignore_rules: - return unit - else: - if self.unit_aliases: - try: - return self.unit_aliases[unit] - except KeyError: - return unit - else: - if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1=unit, disabled=False).order_by('order').first(): - return automation.param_2 - return unit + if not self.ignore_rules: + self.automation = AutomationEngine(self.request, use_cache=cache_mode) def get_unit(self, unit): """ @@ -91,7 +32,10 @@ class IngredientParser: if not unit: return None if len(unit) > 0: - u, created = Unit.objects.get_or_create(name=self.apply_unit_automation(unit), space=self.request.space) + if self.ignore_rules: + u, created = Unit.objects.get_or_create(name=unit.strip(), space=self.request.space) + else: + u, created = Unit.objects.get_or_create(name=self.automation.apply_unit_automation(unit), space=self.request.space) return u return None @@ -104,7 +48,10 @@ class IngredientParser: if not food: return None if len(food) > 0: - f, created = Food.objects.get_or_create(name=self.apply_food_automation(food), space=self.request.space) + if self.ignore_rules: + f, created = Food.objects.get_or_create(name=food.strip(), space=self.request.space) + else: + f, created = Food.objects.get_or_create(name=self.automation.apply_food_automation(food), space=self.request.space) return f return None @@ -133,10 +80,10 @@ class IngredientParser: end = 0 while (end < len(x) and (x[end] in string.digits or ( - (x[end] == '.' or x[end] == ',' or x[end] == '/') - and end + 1 < len(x) - and x[end + 1] in string.digits - ))): + (x[end] == '.' or x[end] == ',' or x[end] == '/') + and end + 1 < len(x) + and x[end + 1] in string.digits + ))): end += 1 if end > 0: if "/" in x[:end]: @@ -160,7 +107,8 @@ class IngredientParser: if unit is not None and unit.strip() == '': unit = None - if unit is not None and (unit.startswith('(') or unit.startswith('-')): # i dont know any unit that starts with ( or - so its likely an alternative like 1L (500ml) Water or 2-3 + if unit is not None and (unit.startswith('(') or unit.startswith( + '-')): # i dont know any unit that starts with ( or - so its likely an alternative like 1L (500ml) Water or 2-3 unit = None note = x return amount, unit, note @@ -230,8 +178,8 @@ class IngredientParser: # if the string contains parenthesis early on remove it and place it at the end # because its likely some kind of note - if re.match('(.){1,6}\s\((.[^\(\)])+\)\s', ingredient): - match = re.search('\((.[^\(])+\)', ingredient) + if re.match('(.){1,6}\\s\\((.[^\\(\\)])+\\)\\s', ingredient): + match = re.search('\\((.[^\\(])+\\)', ingredient) ingredient = ingredient[:match.start()] + ingredient[match.end():] + ' ' + ingredient[match.start():match.end()] # leading spaces before commas result in extra tokens, clean them out @@ -239,12 +187,15 @@ class IngredientParser: # handle "(from) - (to)" amounts by using the minimum amount and adding the range to the description # "10.5 - 200 g XYZ" => "100 g XYZ (10.5 - 200)" - ingredient = re.sub("^(\d+|\d+[\\.,]\d+) - (\d+|\d+[\\.,]\d+) (.*)", "\\1 \\3 (\\1 - \\2)", ingredient) + ingredient = re.sub("^(\\d+|\\d+[\\.,]\\d+) - (\\d+|\\d+[\\.,]\\d+) (.*)", "\\1 \\3 (\\1 - \\2)", ingredient) # if amount and unit are connected add space in between - if re.match('([0-9])+([A-z])+\s', ingredient): + if re.match('([0-9])+([A-z])+\\s', ingredient): ingredient = re.sub(r'(?<=([a-z])|\d)(?=(?(1)\d|[a-z]))', ' ', ingredient) + if not self.ignore_rules: + ingredient = self.automation.apply_transpose_automation(ingredient) + tokens = ingredient.split() # split at each space into tokens if len(tokens) == 1: # there only is one argument, that must be the food @@ -257,6 +208,8 @@ class IngredientParser: # three arguments if it already has a unit there can't be # a fraction for the amount if len(tokens) > 2: + if not self.ignore_rules: + tokens = self.automation.apply_never_unit_automation(tokens) try: if unit is not None: # a unit is already found, no need to try the second argument for a fraction @@ -303,10 +256,11 @@ class IngredientParser: if unit_note not in note: note += ' ' + unit_note - if unit: - unit = self.apply_unit_automation(unit.strip()) + if unit and not self.ignore_rules: + unit = self.automation.apply_unit_automation(unit) - food = self.apply_food_automation(food.strip()) + if food and not self.ignore_rules: + food = self.automation.apply_food_automation(food) if len(food) > Food._meta.get_field('name').max_length: # test if food name is to long # try splitting it at a space and taking only the first arg if len(food.split()) > 1 and len(food.split()[0]) < Food._meta.get_field('name').max_length: diff --git a/cookbook/helper/open_data_importer.py b/cookbook/helper/open_data_importer.py new file mode 100644 index 000000000..e8692fc28 --- /dev/null +++ b/cookbook/helper/open_data_importer.py @@ -0,0 +1,204 @@ +from cookbook.models import (Food, FoodProperty, Property, PropertyType, Supermarket, + SupermarketCategory, SupermarketCategoryRelation, Unit, UnitConversion) + + +class OpenDataImporter: + request = None + data = {} + slug_id_cache = {} + update_existing = False + use_metric = True + + def __init__(self, request, data, update_existing=False, use_metric=True): + self.request = request + self.data = data + self.update_existing = update_existing + self.use_metric = use_metric + + def _update_slug_cache(self, object_class, datatype): + self.slug_id_cache[datatype] = dict(object_class.objects.filter(space=self.request.space, open_data_slug__isnull=False).values_list('open_data_slug', 'id', )) + + def import_units(self): + datatype = 'unit' + + insert_list = [] + for u in list(self.data[datatype].keys()): + insert_list.append(Unit( + name=self.data[datatype][u]['name'], + plural_name=self.data[datatype][u]['plural_name'], + base_unit=self.data[datatype][u]['base_unit'] if self.data[datatype][u]['base_unit'] != '' else None, + open_data_slug=u, + space=self.request.space + )) + + if self.update_existing: + return Unit.objects.bulk_create(insert_list, update_conflicts=True, update_fields=( + 'name', 'plural_name', 'base_unit', 'open_data_slug'), unique_fields=('space', 'name',)) + else: + return Unit.objects.bulk_create(insert_list, update_conflicts=True, update_fields=('open_data_slug',), unique_fields=('space', 'name',)) + + def import_category(self): + datatype = 'category' + + insert_list = [] + for k in list(self.data[datatype].keys()): + insert_list.append(SupermarketCategory( + name=self.data[datatype][k]['name'], + open_data_slug=k, + space=self.request.space + )) + + return SupermarketCategory.objects.bulk_create(insert_list, update_conflicts=True, update_fields=('open_data_slug',), unique_fields=('space', 'name',)) + + def import_property(self): + datatype = 'property' + + insert_list = [] + for k in list(self.data[datatype].keys()): + insert_list.append(PropertyType( + name=self.data[datatype][k]['name'], + unit=self.data[datatype][k]['unit'], + open_data_slug=k, + space=self.request.space + )) + + return PropertyType.objects.bulk_create(insert_list, update_conflicts=True, update_fields=('open_data_slug',), unique_fields=('space', 'name',)) + + def import_supermarket(self): + datatype = 'store' + + self._update_slug_cache(SupermarketCategory, 'category') + insert_list = [] + for k in list(self.data[datatype].keys()): + insert_list.append(Supermarket( + name=self.data[datatype][k]['name'], + open_data_slug=k, + space=self.request.space + )) + + # always add open data slug if matching supermarket is found, otherwise relation might fail + supermarkets = Supermarket.objects.bulk_create(insert_list, unique_fields=('space', 'name',), update_conflicts=True, update_fields=('open_data_slug',)) + self._update_slug_cache(Supermarket, 'store') + + insert_list = [] + for k in list(self.data[datatype].keys()): + relations = [] + order = 0 + for c in self.data[datatype][k]['categories']: + relations.append( + SupermarketCategoryRelation( + supermarket_id=self.slug_id_cache[datatype][k], + category_id=self.slug_id_cache['category'][c], + order=order, + ) + ) + order += 1 + + SupermarketCategoryRelation.objects.bulk_create(relations, ignore_conflicts=True, unique_fields=('supermarket', 'category',)) + + return supermarkets + + def import_food(self): + identifier_list = [] + datatype = 'food' + for k in list(self.data[datatype].keys()): + identifier_list.append(self.data[datatype][k]['name']) + identifier_list.append(self.data[datatype][k]['plural_name']) + + existing_objects_flat = [] + existing_objects = {} + for f in Food.objects.filter(space=self.request.space).filter(name__in=identifier_list).values_list('id', 'name', 'plural_name'): + existing_objects_flat.append(f[1]) + existing_objects_flat.append(f[2]) + existing_objects[f[1]] = f + existing_objects[f[2]] = f + + self._update_slug_cache(Unit, 'unit') + self._update_slug_cache(PropertyType, 'property') + + insert_list = [] + update_list = [] + update_field_list = [] + for k in list(self.data[datatype].keys()): + if not (self.data[datatype][k]['name'] in existing_objects_flat or self.data[datatype][k]['plural_name'] in existing_objects_flat): + insert_list.append({'data': { + 'name': self.data[datatype][k]['name'], + 'plural_name': self.data[datatype][k]['plural_name'] if self.data[datatype][k]['plural_name'] != '' else None, + 'supermarket_category_id': self.slug_id_cache['category'][self.data[datatype][k]['store_category']], + 'fdc_id': self.data[datatype][k]['fdc_id'] if self.data[datatype][k]['fdc_id'] != '' else None, + 'open_data_slug': k, + 'space': self.request.space.id, + }}) + else: + if self.data[datatype][k]['name'] in existing_objects: + existing_food_id = existing_objects[self.data[datatype][k]['name']][0] + else: + existing_food_id = existing_objects[self.data[datatype][k]['plural_name']][0] + + if self.update_existing: + update_field_list = ['name', 'plural_name', 'preferred_unit_id', 'preferred_shopping_unit_id', 'supermarket_category_id', 'fdc_id', 'open_data_slug', ] + update_list.append(Food( + id=existing_food_id, + name=self.data[datatype][k]['name'], + plural_name=self.data[datatype][k]['plural_name'] if self.data[datatype][k]['plural_name'] != '' else None, + supermarket_category_id=self.slug_id_cache['category'][self.data[datatype][k]['store_category']], + fdc_id=self.data[datatype][k]['fdc_id'] if self.data[datatype][k]['fdc_id'] != '' else None, + open_data_slug=k, + )) + else: + update_field_list = ['open_data_slug', ] + update_list.append(Food(id=existing_food_id, open_data_slug=k, )) + + Food.load_bulk(insert_list, None) + if len(update_list) > 0: + Food.objects.bulk_update(update_list, update_field_list) + + self._update_slug_cache(Food, 'food') + + food_property_list = [] + # alias_list = [] + + for k in list(self.data[datatype].keys()): + for fp in self.data[datatype][k]['properties']['type_values']: + # try catch here because somettimes key "k" is not set for he food cache + try: + food_property_list.append(Property( + property_type_id=self.slug_id_cache['property'][fp['property_type']], + property_amount=fp['property_value'], + import_food_id=self.slug_id_cache['food'][k], + space=self.request.space, + )) + except KeyError: + print(str(k) + ' is not in self.slug_id_cache["food"]') + + Property.objects.bulk_create(food_property_list, ignore_conflicts=True, unique_fields=('space', 'import_food_id', 'property_type',)) + + property_food_relation_list = [] + for p in Property.objects.filter(space=self.request.space, import_food_id__isnull=False).values_list('import_food_id', 'id', ): + property_food_relation_list.append(Food.properties.through(food_id=p[0], property_id=p[1])) + + FoodProperty.objects.bulk_create(property_food_relation_list, ignore_conflicts=True, unique_fields=('food_id', 'property_id',)) + + return insert_list + update_list + + def import_conversion(self): + datatype = 'conversion' + + insert_list = [] + for k in list(self.data[datatype].keys()): + # try catch here because sometimes key "k" is not set for he food cache + try: + insert_list.append(UnitConversion( + base_amount=self.data[datatype][k]['base_amount'], + base_unit_id=self.slug_id_cache['unit'][self.data[datatype][k]['base_unit']], + converted_amount=self.data[datatype][k]['converted_amount'], + converted_unit_id=self.slug_id_cache['unit'][self.data[datatype][k]['converted_unit']], + food_id=self.slug_id_cache['food'][self.data[datatype][k]['food']], + open_data_slug=k, + space=self.request.space, + created_by=self.request.user, + )) + except KeyError: + print(str(k) + ' is not in self.slug_id_cache["food"]') + + return UnitConversion.objects.bulk_create(insert_list, ignore_conflicts=True, unique_fields=('space', 'base_unit', 'converted_unit', 'food', 'open_data_slug')) diff --git a/cookbook/helper/permission_helper.py b/cookbook/helper/permission_helper.py index 6afb2e923..889d75055 100644 --- a/cookbook/helper/permission_helper.py +++ b/cookbook/helper/permission_helper.py @@ -4,16 +4,16 @@ from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import user_passes_test from django.core.cache import cache -from django.core.exceptions import ValidationError, ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy from django.utils.translation import gettext as _ -from oauth2_provider.contrib.rest_framework import TokenHasScope, TokenHasReadWriteScope +from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope from oauth2_provider.models import AccessToken from rest_framework import permissions from rest_framework.permissions import SAFE_METHODS -from cookbook.models import ShareLink, Recipe, UserSpace +from cookbook.models import Recipe, ShareLink, UserSpace def get_allowed_groups(groups_required): @@ -255,9 +255,6 @@ class CustomIsShared(permissions.BasePermission): return request.user.is_authenticated def has_object_permission(self, request, view, obj): - # # temporary hack to make old shopping list work with new shopping list - # if obj.__class__.__name__ in ['ShoppingList', 'ShoppingListEntry']: - # return is_object_shared(request.user, obj) or obj.created_by in list(request.user.get_shopping_share()) return is_object_shared(request.user, obj) @@ -322,7 +319,8 @@ class CustomRecipePermission(permissions.BasePermission): def has_permission(self, request, view): # user is either at least a guest or a share link is given and the request is safe share = request.query_params.get('share', None) - return has_group_permission(request.user, ['guest']) or (share and request.method in SAFE_METHODS and 'pk' in view.kwargs) + return ((has_group_permission(request.user, ['guest']) and request.method in SAFE_METHODS) or has_group_permission( + request.user, ['user'])) or (share and request.method in SAFE_METHODS and 'pk' in view.kwargs) def has_object_permission(self, request, view, obj): share = request.query_params.get('share', None) @@ -332,7 +330,8 @@ class CustomRecipePermission(permissions.BasePermission): if obj.private: return ((obj.created_by == request.user) or (request.user in obj.shared.all())) and obj.space == request.space else: - return has_group_permission(request.user, ['guest']) and obj.space == request.space + return ((has_group_permission(request.user, ['guest']) and request.method in SAFE_METHODS) + or has_group_permission(request.user, ['user'])) and obj.space == request.space class CustomUserPermission(permissions.BasePermission): @@ -361,7 +360,7 @@ class CustomTokenHasScope(TokenHasScope): """ def has_permission(self, request, view): - if type(request.auth) == AccessToken: + if isinstance(request.auth, AccessToken): return super().has_permission(request, view) else: return request.user.is_authenticated @@ -375,7 +374,7 @@ class CustomTokenHasReadWriteScope(TokenHasReadWriteScope): """ def has_permission(self, request, view): - if type(request.auth) == AccessToken: + if isinstance(request.auth, AccessToken): return super().has_permission(request, view) else: return True @@ -434,3 +433,10 @@ def switch_user_active_space(user, space): return us except ObjectDoesNotExist: return None + + +class IsReadOnlyDRF(permissions.BasePermission): + message = 'You cannot interact with this object as it is not owned by you!' + + def has_permission(self, request, view): + return request.method in SAFE_METHODS diff --git a/cookbook/helper/property_helper.py b/cookbook/helper/property_helper.py new file mode 100644 index 000000000..04521c658 --- /dev/null +++ b/cookbook/helper/property_helper.py @@ -0,0 +1,74 @@ +from django.core.cache import caches + +from cookbook.helper.cache_helper import CacheHelper +from cookbook.helper.unit_conversion_helper import UnitConversionHelper +from cookbook.models import PropertyType + + +class FoodPropertyHelper: + space = None + + def __init__(self, space): + """ + Helper to perform food property calculations + :param space: space to limit scope to + """ + self.space = space + + def calculate_recipe_properties(self, recipe): + """ + Calculate all food properties for a given recipe. + :param recipe: recipe to calculate properties for + :return: dict of with property keys and total/food values for each property available + """ + ingredients = [] + computed_properties = {} + + for s in recipe.steps.all(): + ingredients += s.ingredients.all() + + property_types = caches['default'].get(CacheHelper(self.space).PROPERTY_TYPE_CACHE_KEY, None) + + if not property_types: + property_types = PropertyType.objects.filter(space=self.space).all() + # cache is cleared on property type save signal so long duration is fine + caches['default'].set(CacheHelper(self.space).PROPERTY_TYPE_CACHE_KEY, property_types, 60 * 60) + + for fpt in property_types: + computed_properties[fpt.id] = {'id': fpt.id, 'name': fpt.name, 'description': fpt.description, + 'unit': fpt.unit, 'order': fpt.order, 'food_values': {}, 'total_value': 0, 'missing_value': False} + + uch = UnitConversionHelper(self.space) + + for i in ingredients: + if i.food is not None: + conversions = uch.get_conversions(i) + for pt in property_types: + found_property = False + if i.food.properties_food_amount == 0 or i.food.properties_food_unit is None: + computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': 0} + computed_properties[pt.id]['missing_value'] = i.food.properties_food_unit is None + else: + for p in i.food.properties.all(): + if p.property_type == pt: + for c in conversions: + if c.unit == i.food.properties_food_unit: + found_property = True + computed_properties[pt.id]['total_value'] += (c.amount / i.food.properties_food_amount) * p.property_amount + computed_properties[pt.id]['food_values'] = self.add_or_create( + computed_properties[p.property_type.id]['food_values'], c.food.id, (c.amount / i.food.properties_food_amount) * p.property_amount, c.food) + if not found_property: + computed_properties[pt.id]['missing_value'] = True + computed_properties[pt.id]['food_values'][i.food.id] = {'id': i.food.id, 'food': i.food.name, 'value': 0} + + return computed_properties + + # small dict helper to add to existing key or create new, probably a better way of doing this + # TODO move to central helper ? + @staticmethod + def add_or_create(d, key, value, food): + if key in d: + d[key]['value'] += value + else: + d[key] = {'id': food.id, 'food': food.name, 'value': value} + return d diff --git a/cookbook/helper/recipe_html_import.py b/cookbook/helper/recipe_html_import.py deleted file mode 100644 index 95f115b76..000000000 --- a/cookbook/helper/recipe_html_import.py +++ /dev/null @@ -1,191 +0,0 @@ -# import json -# import re -# from json import JSONDecodeError -# from urllib.parse import unquote - -# from bs4 import BeautifulSoup -# from bs4.element import Tag -# from recipe_scrapers import scrape_html, scrape_me -# from recipe_scrapers._exceptions import NoSchemaFoundInWildMode -# from recipe_scrapers._utils import get_host_name, normalize_string - -# from cookbook.helper import recipe_url_import as helper -# from cookbook.helper.scrapers.scrapers import text_scraper - - -# def get_recipe_from_source(text, url, request): -# def build_node(k, v): -# if isinstance(v, dict): -# node = { -# 'name': k, -# 'value': k, -# 'children': get_children_dict(v) -# } -# elif isinstance(v, list): -# node = { -# 'name': k, -# 'value': k, -# 'children': get_children_list(v) -# } -# else: -# node = { -# 'name': k + ": " + normalize_string(str(v)), -# 'value': normalize_string(str(v)) -# } -# return node - -# def get_children_dict(children): -# kid_list = [] -# for k, v in children.items(): -# kid_list.append(build_node(k, v)) -# return kid_list - -# def get_children_list(children): -# kid_list = [] -# for kid in children: -# if type(kid) == list: -# node = { -# 'name': "unknown list", -# 'value': "unknown list", -# 'children': get_children_list(kid) -# } -# kid_list.append(node) -# elif type(kid) == dict: -# for k, v in kid.items(): -# kid_list.append(build_node(k, v)) -# else: -# kid_list.append({ -# 'name': normalize_string(str(kid)), -# 'value': normalize_string(str(kid)) -# }) -# return kid_list - -# recipe_tree = [] -# parse_list = [] -# soup = BeautifulSoup(text, "html.parser") -# html_data = get_from_html(soup) -# images = get_images_from_source(soup, url) -# text = unquote(text) -# scrape = None - -# if url and not text: -# try: -# scrape = scrape_me(url_path=url, wild_mode=True) -# except(NoSchemaFoundInWildMode): -# pass - -# if not scrape: -# try: -# parse_list.append(remove_graph(json.loads(text))) -# if not url and 'url' in parse_list[0]: -# url = parse_list[0]['url'] -# scrape = text_scraper("", url=url) - -# except JSONDecodeError: -# for el in soup.find_all('script', type='application/ld+json'): -# el = remove_graph(el) -# if not url and 'url' in el: -# url = el['url'] -# if type(el) == list: -# for le in el: -# parse_list.append(le) -# elif type(el) == dict: -# parse_list.append(el) -# for el in soup.find_all(type='application/json'): -# el = remove_graph(el) -# if type(el) == list: -# for le in el: -# parse_list.append(le) -# elif type(el) == dict: -# parse_list.append(el) -# scrape = text_scraper(text, url=url) - -# recipe_json = helper.get_from_scraper(scrape, request) - -# # TODO: DEPRECATE recipe_tree & html_data. first validate it isn't used anywhere -# for el in parse_list: -# temp_tree = [] -# if isinstance(el, Tag): -# try: -# el = json.loads(el.string) -# except TypeError: -# continue - -# for k, v in el.items(): -# if isinstance(v, dict): -# node = { -# 'name': k, -# 'value': k, -# 'children': get_children_dict(v) -# } -# elif isinstance(v, list): -# node = { -# 'name': k, -# 'value': k, -# 'children': get_children_list(v) -# } -# else: -# node = { -# 'name': k + ": " + normalize_string(str(v)), -# 'value': normalize_string(str(v)) -# } -# temp_tree.append(node) - -# if '@type' in el and el['@type'] == 'Recipe': -# recipe_tree += [{'name': 'ld+json', 'children': temp_tree}] -# else: -# recipe_tree += [{'name': 'json', 'children': temp_tree}] - -# return recipe_json, recipe_tree, html_data, images - - -# def get_from_html(soup): -# INVISIBLE_ELEMS = ('style', 'script', 'head', 'title') -# html = [] -# for s in soup.strings: -# if ((s.parent.name not in INVISIBLE_ELEMS) and (len(s.strip()) > 0)): -# html.append(s) -# return html - - -# def get_images_from_source(soup, url): -# sources = ['src', 'srcset', 'data-src'] -# images = [] -# img_tags = soup.find_all('img') -# if url: -# site = get_host_name(url) -# prot = url.split(':')[0] - -# urls = [] -# for img in img_tags: -# for src in sources: -# try: -# urls.append(img[src]) -# except KeyError: -# pass - -# for u in urls: -# u = u.split('?')[0] -# filename = re.search(r'/([\w_-]+[.](jpg|jpeg|gif|png))$', u) -# if filename: -# if (('http' not in u) and (url)): -# # sometimes an image source can be relative -# # if it is provide the base url -# u = '{}://{}{}'.format(prot, site, u) -# if 'http' in u: -# images.append(u) -# return images - - -# def remove_graph(el): -# # recipes type might be wrapped in @graph type -# if isinstance(el, Tag): -# try: -# el = json.loads(el.string) -# if '@graph' in el: -# for x in el['@graph']: -# if '@type' in x and x['@type'] == 'Recipe': -# el = x -# except (TypeError, JSONDecodeError): -# pass -# return el diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py index 9a590480d..4f4ab8553 100644 --- a/cookbook/helper/recipe_search.py +++ b/cookbook/helper/recipe_search.py @@ -1,14 +1,11 @@ import json -from collections import Counter from datetime import date, timedelta from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity from django.core.cache import cache -from django.core.cache import caches -from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When, FilteredRelation) +from django.db.models import Avg, Case, Count, Exists, F, Max, OuterRef, Q, Subquery, Value, When from django.db.models.functions import Coalesce, Lower, Substr from django.utils import timezone, translation -from django.utils.translation import gettext as _ from cookbook.helper.HelperFunctions import Round, str2bool from cookbook.managers import DICTIONARY @@ -17,7 +14,6 @@ from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, Searc from recipes import settings -# TODO create extensive tests to make sure ORs ANDs and various filters, sorting, etc work as expected # TODO consider creating a simpleListRecipe API that only includes minimum of recipe info and minimal filtering class RecipeSearch(): _postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql'] @@ -26,11 +22,17 @@ class RecipeSearch(): self._request = request self._queryset = None if f := params.get('filter', None): - custom_filter = CustomFilter.objects.filter(id=f, space=self._request.space).filter(Q(created_by=self._request.user) | - Q(shared=self._request.user) | Q(recipebook__shared=self._request.user)).first() + custom_filter = ( + CustomFilter.objects.filter(id=f, space=self._request.space) + .filter(Q(created_by=self._request.user) | Q(shared=self._request.user) | Q(recipebook__shared=self._request.user)) + .first() + ) if custom_filter: self._params = {**json.loads(custom_filter.search)} self._original_params = {**(params or {})} + # json.loads casts rating as an integer, expecting string + if isinstance(self._params.get('rating', None), int): + self._params['rating'] = str(self._params['rating']) else: self._params = {**(params or {})} else: @@ -45,7 +47,8 @@ class RecipeSearch(): cache.set(CACHE_KEY, self._search_prefs, timeout=10) else: self._search_prefs = SearchPreference() - self._string = self._params.get('query').strip() if self._params.get('query', None) else None + self._string = self._params.get('query').strip( + ) if self._params.get('query', None) else None self._rating = self._params.get('rating', None) self._keywords = { 'or': self._params.get('keywords_or', None) or self._params.get('keywords', None), @@ -74,7 +77,8 @@ class RecipeSearch(): self._random = str2bool(self._params.get('random', False)) self._new = str2bool(self._params.get('new', False)) self._num_recent = int(self._params.get('num_recent', 0)) - self._include_children = str2bool(self._params.get('include_children', None)) + self._include_children = str2bool( + self._params.get('include_children', None)) self._timescooked = self._params.get('timescooked', None) self._cookedon = self._params.get('cookedon', None) self._createdon = self._params.get('createdon', None) @@ -82,9 +86,9 @@ class RecipeSearch(): self._viewedon = self._params.get('viewedon', None) self._makenow = self._params.get('makenow', None) # this supports hidden feature to find recipes missing X ingredients - if type(self._makenow) == bool and self._makenow == True: + if isinstance(self._makenow, bool) and self._makenow == True: self._makenow = 0 - elif type(self._makenow) == str and self._makenow in ["yes", "true"]: + elif isinstance(self._makenow, str) and self._makenow in ["yes", "true"]: self._makenow = 0 else: try: @@ -141,7 +145,7 @@ class RecipeSearch(): self.unit_filters(units=self._units) self._makenow_filter(missing=self._makenow) self.string_filters(string=self._string) - return self._queryset.filter(space=self._request.space).distinct().order_by(*self.orderby) + return self._queryset.filter(space=self._request.space).order_by(*self.orderby) def _sort_includes(self, *args): for x in args: @@ -157,7 +161,7 @@ class RecipeSearch(): else: order = [] # TODO add userpreference for default sort order and replace '-favorite' - default_order = ['-name'] + default_order = ['name'] # recent and new_recipe are always first; they float a few recipes to the top if self._num_recent: order += ['-recent'] @@ -166,7 +170,6 @@ class RecipeSearch(): # if a sort order is provided by user - use that order if self._sort_order: - if not isinstance(self._sort_order, list): order += [self._sort_order] else: @@ -182,8 +185,10 @@ class RecipeSearch(): # otherwise sort by the remaining order_by attributes or favorite by default else: order += default_order - order[:] = [Lower('name').asc() if x == 'name' else x for x in order] - order[:] = [Lower('name').desc() if x == '-name' else x for x in order] + order[:] = [Lower('name').asc() if x == + 'name' else x for x in order] + order[:] = [Lower('name').desc() if x == + '-name' else x for x in order] self.orderby = order def string_filters(self, string=None): @@ -200,7 +205,8 @@ class RecipeSearch(): for f in self._filters: query_filter |= f - self._queryset = self._queryset.filter(query_filter).distinct() # this creates duplicate records which can screw up other aggregates, see makenow for workaround + # this creates duplicate records which can screw up other aggregates, see makenow for workaround + self._queryset = self._queryset.filter(query_filter).distinct() if self._fulltext_include: if self._fuzzy_match is None: self._queryset = self._queryset.annotate(score=Coalesce(Max(self.search_rank), 0.0)) @@ -228,12 +234,13 @@ class RecipeSearch(): default = timezone.now() - timedelta(days=100000) else: default = timezone.now() - self._queryset = self._queryset.annotate(lastcooked=Coalesce( - Max(Case(When(cooklog__created_by=self._request.user, cooklog__space=self._request.space, then='cooklog__created_at'))), Value(default))) + self._queryset = self._queryset.annotate( + lastcooked=Coalesce(Max(Case(When(cooklog__created_by=self._request.user, cooklog__space=self._request.space, then='cooklog__created_at'))), Value(default)) + ) if cooked_date is None: return - cooked_date = date(*[int(x) for x in cooked_date.split('-') if x != '']) + cooked_date = date(*[int(x)for x in cooked_date.split('-') if x != '']) if lessthan: self._queryset = self._queryset.filter(lastcooked__date__lte=cooked_date).exclude(lastcooked=default) @@ -254,7 +261,7 @@ class RecipeSearch(): if updated_date is None: return lessthan = '-' in updated_date[:1] - updated_date = date(*[int(x) for x in updated_date.split('-') if x != '']) + updated_date = date(*[int(x)for x in updated_date.split('-') if x != '']) if lessthan: self._queryset = self._queryset.filter(updated_at__date__lte=updated_date) else: @@ -263,12 +270,13 @@ class RecipeSearch(): def _viewed_on_filter(self, viewed_date=None): if self._sort_includes('lastviewed') or viewed_date: longTimeAgo = timezone.now() - timedelta(days=100000) - self._queryset = self._queryset.annotate(lastviewed=Coalesce( - Max(Case(When(viewlog__created_by=self._request.user, viewlog__space=self._request.space, then='viewlog__created_at'))), Value(longTimeAgo))) + self._queryset = self._queryset.annotate( + lastviewed=Coalesce(Max(Case(When(viewlog__created_by=self._request.user, viewlog__space=self._request.space, then='viewlog__created_at'))), Value(longTimeAgo)) + ) if viewed_date is None: return lessthan = '-' in viewed_date[:1] - viewed_date = date(*[int(x) for x in viewed_date.split('-') if x != '']) + viewed_date = date(*[int(x)for x in viewed_date.split('-') if x != '']) if lessthan: self._queryset = self._queryset.filter(lastviewed__date__lte=viewed_date).exclude(lastviewed=longTimeAgo) @@ -279,9 +287,11 @@ class RecipeSearch(): # TODO make new days a user-setting if not self._new: return - self._queryset = ( - self._queryset.annotate(new_recipe=Case( - When(created_at__gte=(timezone.now() - timedelta(days=new_days)), then=('pk')), default=Value(0), )) + self._queryset = self._queryset.annotate( + new_recipe=Case( + When(created_at__gte=(timezone.now() - timedelta(days=new_days)), then=('pk')), + default=Value(0), + ) ) def _recently_viewed(self, num_recent=None): @@ -291,19 +301,25 @@ class RecipeSearch(): Max(Case(When(viewlog__created_by=self._request.user, viewlog__space=self._request.space, then='viewlog__pk'))), Value(0))) return - num_recent_recipes = ViewLog.objects.filter(created_by=self._request.user, space=self._request.space).values( - 'recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent] + num_recent_recipes = ( + ViewLog.objects.filter(created_by=self._request.user, space=self._request.space) + .values('recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent] + ) self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0))) def _favorite_recipes(self, times_cooked=None): if self._sort_includes('favorite') or times_cooked: - less_than = '-' in (times_cooked or []) or not self._sort_includes('-favorite') + less_than = '-' in (times_cooked or []) and not self._sort_includes('-favorite') if less_than: default = 1000 else: default = 0 - favorite_recipes = CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk') - ).values('recipe').annotate(count=Count('pk', distinct=True)).values('count') + favorite_recipes = ( + CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk')) + .values('recipe') + .annotate(count=Count('pk', distinct=True)) + .values('count') + ) self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default)) if times_cooked is None: return @@ -311,7 +327,7 @@ class RecipeSearch(): if times_cooked == '0': self._queryset = self._queryset.filter(favorite=0) elif less_than: - self._queryset = self._queryset.filter(favorite__lte=int(times_cooked[1:])).exclude(favorite=0) + self._queryset = self._queryset.filter(favorite__lte=int(times_cooked.replace('-', ''))).exclude(favorite=0) else: self._queryset = self._queryset.filter(favorite__gte=int(times_cooked)) @@ -393,8 +409,9 @@ class RecipeSearch(): def rating_filter(self, rating=None): if rating or self._sort_includes('rating'): - lessthan = self._sort_includes('-rating') or '-' in (rating or []) - if lessthan: + lessthan = '-' in (rating or []) + reverse = 'rating' in (self._sort_order or []) and '-rating' not in (self._sort_order or []) + if lessthan or reverse: default = 100 else: default = 0 @@ -446,7 +463,7 @@ class RecipeSearch(): if not steps: return if not isinstance(steps, list): - steps = [unistepsts] + steps = [steps] self._queryset = self._queryset.filter(steps__id__in=steps) def build_fulltext_filters(self, string=None): @@ -503,26 +520,33 @@ class RecipeSearch(): trigram += TrigramSimilarity(f, self._string) else: trigram = TrigramSimilarity(f, self._string) - self._fuzzy_match = Recipe.objects.annotate(trigram=trigram).distinct( - ).annotate(simularity=Max('trigram')).values('id', 'simularity').filter(simularity__gt=self._search_prefs.trigram_threshold) + self._fuzzy_match = ( + Recipe.objects.annotate(trigram=trigram) + .distinct() + .annotate(simularity=Max('trigram')) + .values('id', 'simularity') + .filter(simularity__gt=self._search_prefs.trigram_threshold) + ) self._filters += [Q(pk__in=self._fuzzy_match.values('pk'))] def _makenow_filter(self, missing=None): - if missing is None or (type(missing) == bool and missing == False): + if missing is None or (isinstance(missing, bool) and missing == False): return shopping_users = [*self._request.user.get_shopping_share(), self._request.user] onhand_filter = ( - Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand - | Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand - | Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users)) - | Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users)) + Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand + # or substitute food onhand + | Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) + | Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users)) + | Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users)) ) makenow_recipes = Recipe.objects.annotate( count_food=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__isnull=False), distinct=True), count_onhand=Count('steps__ingredients__food__pk', filter=onhand_filter, distinct=True), - count_ignore_shopping=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__ignore_shopping=True, - steps__ingredients__food__recipe__isnull=True), distinct=True), + count_ignore_shopping=Count( + 'steps__ingredients__food__pk', filter=Q(steps__ingredients__food__ignore_shopping=True, steps__ingredients__food__recipe__isnull=True), distinct=True + ), has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users), then=Value(1)), default=Value(0)), has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users), then=Value(1)), default=Value(0)) ).annotate(missingfood=F('count_food') - F('count_onhand') - F('count_ignore_shopping')).filter(missingfood__lte=missing) @@ -530,236 +554,28 @@ class RecipeSearch(): @staticmethod def __children_substitute_filter(shopping_users=None): - children_onhand_subquery = Food.objects.filter( - path__startswith=OuterRef('path'), - depth__gt=OuterRef('depth'), - onhand_users__in=shopping_users + children_onhand_subquery = Food.objects.filter(path__startswith=OuterRef('path'), depth__gt=OuterRef('depth'), onhand_users__in=shopping_users) + return ( + Food.objects.exclude( # list of foods that are onhand and children of: foods that are not onhand and are set to use children as substitutes + Q(onhand_users__in=shopping_users) | Q(ignore_shopping=True, recipe__isnull=True) | Q(substitute__onhand_users__in=shopping_users) + ) + .exclude(depth=1, numchild=0) + .filter(substitute_children=True) + .annotate(child_onhand_count=Exists(children_onhand_subquery)) + .filter(child_onhand_count=True) ) - return Food.objects.exclude( # list of foods that are onhand and children of: foods that are not onhand and are set to use children as substitutes - Q(onhand_users__in=shopping_users) - | Q(ignore_shopping=True, recipe__isnull=True) - | Q(substitute__onhand_users__in=shopping_users) - ).exclude(depth=1, numchild=0 - ).filter(substitute_children=True - ).annotate(child_onhand_count=Exists(children_onhand_subquery) - ).filter(child_onhand_count=True) @staticmethod def __sibling_substitute_filter(shopping_users=None): sibling_onhand_subquery = Food.objects.filter( - path__startswith=Substr(OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)), - depth=OuterRef('depth'), - onhand_users__in=shopping_users + path__startswith=Substr(OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)), depth=OuterRef('depth'), onhand_users__in=shopping_users ) - return Food.objects.exclude( # list of foods that are onhand and siblings of: foods that are not onhand and are set to use siblings as substitutes - Q(onhand_users__in=shopping_users) - | Q(ignore_shopping=True, recipe__isnull=True) - | Q(substitute__onhand_users__in=shopping_users) - ).exclude(depth=1, numchild=0 - ).filter(substitute_siblings=True - ).annotate(sibling_onhand=Exists(sibling_onhand_subquery) - ).filter(sibling_onhand=True) - - -class RecipeFacet(): - class CacheEmpty(Exception): - pass - - def __init__(self, request, queryset=None, hash_key=None, cache_timeout=3600): - if hash_key is None and queryset is None: - raise ValueError(_("One of queryset or hash_key must be provided")) - - self._request = request - self._queryset = queryset - self.hash_key = hash_key or str(hash(self._queryset.query)) - self._SEARCH_CACHE_KEY = f"recipes_filter_{self.hash_key}" - self._cache_timeout = cache_timeout - self._cache = caches['default'].get(self._SEARCH_CACHE_KEY, {}) - if self._cache is None and self._queryset is None: - raise self.CacheEmpty("No queryset provided and cache empty") - - self.Keywords = self._cache.get('Keywords', None) - self.Foods = self._cache.get('Foods', None) - self.Books = self._cache.get('Books', None) - self.Ratings = self._cache.get('Ratings', None) - # TODO Move Recent to recipe annotation/serializer: requrires change in RecipeSearch(), RecipeSearchView.vue and serializer - self.Recent = self._cache.get('Recent', None) - - if self._queryset is not None: - self._recipe_list = list(self._queryset.values_list('id', flat=True)) - self._search_params = { - 'keyword_list': self._request.query_params.getlist('keywords', []), - 'food_list': self._request.query_params.getlist('foods', []), - 'book_list': self._request.query_params.getlist('book', []), - 'search_keywords_or': str2bool(self._request.query_params.get('keywords_or', True)), - 'search_foods_or': str2bool(self._request.query_params.get('foods_or', True)), - 'search_books_or': str2bool(self._request.query_params.get('books_or', True)), - 'space': self._request.space, - } - elif self.hash_key is not None: - self._recipe_list = self._cache.get('recipe_list', []) - self._search_params = { - 'keyword_list': self._cache.get('keyword_list', None), - 'food_list': self._cache.get('food_list', None), - 'book_list': self._cache.get('book_list', None), - 'search_keywords_or': self._cache.get('search_keywords_or', None), - 'search_foods_or': self._cache.get('search_foods_or', None), - 'search_books_or': self._cache.get('search_books_or', None), - 'space': self._cache.get('space', None), - } - - self._cache = { - **self._search_params, - 'recipe_list': self._recipe_list, - 'Ratings': self.Ratings, - 'Recent': self.Recent, - 'Keywords': self.Keywords, - 'Foods': self.Foods, - 'Books': self.Books - - } - caches['default'].set(self._SEARCH_CACHE_KEY, self._cache, self._cache_timeout) - - def get_facets(self, from_cache=False): - if from_cache: - return { - 'cache_key': self.hash_key or '', - 'Ratings': self.Ratings or {}, - 'Recent': self.Recent or [], - 'Keywords': self.Keywords or [], - 'Foods': self.Foods or [], - 'Books': self.Books or [] - } - return { - 'cache_key': self.hash_key, - 'Ratings': self.get_ratings(), - 'Recent': self.get_recent(), - 'Keywords': self.get_keywords(), - 'Foods': self.get_foods(), - 'Books': self.get_books() - } - - def set_cache(self, key, value): - self._cache = {**self._cache, key: value} - caches['default'].set( - self._SEARCH_CACHE_KEY, - self._cache, - self._cache_timeout + return ( + Food.objects.exclude( # list of foods that are onhand and siblings of: foods that are not onhand and are set to use siblings as substitutes + Q(onhand_users__in=shopping_users) | Q(ignore_shopping=True, recipe__isnull=True) | Q(substitute__onhand_users__in=shopping_users) + ) + .exclude(depth=1, numchild=0) + .filter(substitute_siblings=True) + .annotate(sibling_onhand=Exists(sibling_onhand_subquery)) + .filter(sibling_onhand=True) ) - - def get_books(self): - if self.Books is None: - self.Books = [] - return self.Books - - def get_keywords(self): - if self.Keywords is None: - if self._search_params['search_keywords_or']: - keywords = Keyword.objects.filter(space=self._request.space).distinct() - else: - keywords = Keyword.objects.filter(Q(recipe__in=self._recipe_list) | Q(depth=1)).filter(space=self._request.space).distinct() - - # set keywords to root objects only - keywords = self._keyword_queryset(keywords) - self.Keywords = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(keywords)] - self.set_cache('Keywords', self.Keywords) - return self.Keywords - - def get_foods(self): - if self.Foods is None: - # # if using an OR search, will annotate all keywords, otherwise, just those that appear in results - if self._search_params['search_foods_or']: - foods = Food.objects.filter(space=self._request.space).distinct() - else: - foods = Food.objects.filter(Q(ingredient__step__recipe__in=self._recipe_list) | Q(depth=1)).filter(space=self._request.space).distinct() - - # set keywords to root objects only - foods = self._food_queryset(foods) - - self.Foods = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(foods)] - self.set_cache('Foods', self.Foods) - return self.Foods - - def get_books(self): - if self.Books is None: - self.Books = [] - return self.Books - - def get_ratings(self): - if self.Ratings is None: - if not self._request.space.demo and self._request.space.show_facet_count: - if self._queryset is None: - self._queryset = Recipe.objects.filter(id__in=self._recipe_list) - rating_qs = self._queryset.annotate(rating=Round(Avg(Case(When(cooklog__created_by=self._request.user, then='cooklog__rating'), default=Value(0))))) - self.Ratings = dict(Counter(r.rating for r in rating_qs)) - else: - self.Rating = {} - self.set_cache('Ratings', self.Ratings) - return self.Ratings - - def get_recent(self): - if self.Recent is None: - # TODO make days of recent recipe a setting - recent_recipes = ViewLog.objects.filter(created_by=self._request.user, space=self._request.space, created_at__gte=timezone.now() - timedelta(days=14) - ).values_list('recipe__pk', flat=True) - self.Recent = list(recent_recipes) - self.set_cache('Recent', self.Recent) - return self.Recent - - def add_food_children(self, id): - try: - food = Food.objects.get(id=id) - nodes = food.get_ancestors() - except Food.DoesNotExist: - return self.get_facets() - foods = self._food_queryset(food.get_children(), food) - deep_search = self.Foods - for node in nodes: - index = next((i for i, x in enumerate(deep_search) if x["id"] == node.id), None) - deep_search = deep_search[index]['children'] - index = next((i for i, x in enumerate(deep_search) if x["id"] == food.id), None) - deep_search[index]['children'] = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(foods)] - self.set_cache('Foods', self.Foods) - return self.get_facets() - - def add_keyword_children(self, id): - try: - keyword = Keyword.objects.get(id=id) - nodes = keyword.get_ancestors() - except Keyword.DoesNotExist: - return self.get_facets() - keywords = self._keyword_queryset(keyword.get_children(), keyword) - deep_search = self.Keywords - for node in nodes: - index = next((i for i, x in enumerate(deep_search) if x["id"] == node.id), None) - deep_search = deep_search[index]['children'] - index = next((i for i, x in enumerate(deep_search) if x["id"] == keyword.id), None) - deep_search[index]['children'] = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(keywords)] - self.set_cache('Keywords', self.Keywords) - return self.get_facets() - - def _recipe_count_queryset(self, field, depth=1, steplen=4): - return Recipe.objects.filter(**{f'{field}__path__startswith': OuterRef('path'), f'{field}__depth__gte': depth}, id__in=self._recipe_list, space=self._request.space - ).annotate(count=Coalesce(Func('pk', function='Count'), 0)).values('count') - - def _keyword_queryset(self, queryset, keyword=None): - depth = getattr(keyword, 'depth', 0) + 1 - steplen = depth * Keyword.steplen - - if not self._request.space.demo and self._request.space.show_facet_count: - return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('keywords', depth, steplen)), 0) - ).filter(depth=depth, count__gt=0 - ).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200] - else: - return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc()) - - def _food_queryset(self, queryset, food=None): - depth = getattr(food, 'depth', 0) + 1 - steplen = depth * Food.steplen - - if not self._request.space.demo and self._request.space.show_facet_count: - return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('steps__ingredients__food', depth, steplen)), 0) - ).filter(depth__lte=depth, count__gt=0 - ).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200] - else: - return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc()) diff --git a/cookbook/helper/recipe_url_import.py b/cookbook/helper/recipe_url_import.py index 6aec16b03..b863e8bdb 100644 --- a/cookbook/helper/recipe_url_import.py +++ b/cookbook/helper/recipe_url_import.py @@ -1,7 +1,6 @@ -import random import re +import traceback from html import unescape -from unicodedata import decomposition from django.utils.dateparse import parse_duration from django.utils.translation import gettext as _ @@ -10,17 +9,37 @@ from isodate.isoerror import ISO8601Error from pytube import YouTube from recipe_scrapers._utils import get_host_name, get_minutes -from cookbook.helper import recipe_url_import as helper +from cookbook.helper.automation_helper import AutomationEngine from cookbook.helper.ingredient_parser import IngredientParser -from cookbook.models import Keyword, Automation - - -# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR +from cookbook.models import Automation, Keyword, PropertyType def get_from_scraper(scrape, request): # converting the scrape_me object to the existing json format based on ld+json - recipe_json = {} + + recipe_json = { + 'steps': [], + 'internal': True + } + keywords = [] + + # assign source URL + try: + source_url = scrape.canonical_url() + except Exception: + try: + source_url = scrape.url + except Exception: + pass + if source_url: + recipe_json['source_url'] = source_url + try: + keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0]) + except Exception: + recipe_json['source_url'] = '' + + automation_engine = AutomationEngine(request, source=recipe_json.get('source_url')) + # assign recipe name try: recipe_json['name'] = parse_name(scrape.title()[:128] or None) except Exception: @@ -31,6 +50,13 @@ def get_from_scraper(scrape, request): except Exception: recipe_json['name'] = '' + if isinstance(recipe_json['name'], list) and len(recipe_json['name']) > 0: + recipe_json['name'] = recipe_json['name'][0] + + recipe_json['name'] = automation_engine.apply_regex_replace_automation(recipe_json['name'], Automation.NAME_REPLACE) + + # assign recipe description + # TODO notify user about limit if reached - >256 description will be truncated try: description = scrape.description() or None except Exception: @@ -41,16 +67,20 @@ def get_from_scraper(scrape, request): except Exception: description = '' - recipe_json['internal'] = True + recipe_json['description'] = parse_description(description) + recipe_json['description'] = automation_engine.apply_regex_replace_automation(recipe_json['description'], Automation.DESCRIPTION_REPLACE) + # assign servings attributes try: - servings = scrape.schema.data.get('recipeYield') or 1 # dont use scrape.yields() as this will always return "x servings" or "x items", should be improved in scrapers directly + # dont use scrape.yields() as this will always return "x servings" or "x items", should be improved in scrapers directly + servings = scrape.schema.data.get('recipeYield') or 1 except Exception: servings = 1 recipe_json['servings'] = parse_servings(servings) recipe_json['servings_text'] = parse_servings_text(servings) + # assign time attributes try: recipe_json['working_time'] = get_minutes(scrape.prep_time()) or 0 except Exception: @@ -75,6 +105,7 @@ def get_from_scraper(scrape, request): except Exception: pass + # assign image try: recipe_json['image'] = parse_image(scrape.image()) or None except Exception: @@ -85,7 +116,7 @@ def get_from_scraper(scrape, request): except Exception: recipe_json['image'] = '' - keywords = [] + # assign keywords try: if scrape.schema.data.get("keywords"): keywords += listify_keywords(scrape.schema.data.get("keywords")) @@ -110,54 +141,32 @@ def get_from_scraper(scrape, request): except Exception: pass - try: - source_url = scrape.canonical_url() - except Exception: - try: - source_url = scrape.url - except Exception: - pass - if source_url: - recipe_json['source_url'] = source_url - try: - keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0]) - except Exception: - recipe_json['source_url'] = '' - try: if scrape.author(): keywords.append(scrape.author()) - except: + except Exception: pass try: - recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request.space) + recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request) except AttributeError: recipe_json['keywords'] = keywords ingredient_parser = IngredientParser(request, True) - recipe_json['steps'] = [] + # assign steps try: for i in parse_instructions(scrape.instructions()): - recipe_json['steps'].append({'instruction': i, 'ingredients': [], }) + recipe_json['steps'].append({'instruction': i, 'ingredients': [], 'show_ingredients_table': request.user.userpreference.show_step_ingredients, }) except Exception: pass if len(recipe_json['steps']) == 0: recipe_json['steps'].append({'instruction': '', 'ingredients': [], }) - parsed_description = parse_description(description) - # TODO notify user about limit if reached - # limits exist to limit the attack surface for dos style attacks - automations = Automation.objects.filter(type=Automation.DESCRIPTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').all().order_by('order')[:512] - for a in automations: - if re.match(a.param_1, (recipe_json['source_url'])[:512]): - parsed_description = re.sub(a.param_2, a.param_3, parsed_description, count=1) - - if len(parsed_description) > 256: # split at 256 as long descriptions don't look good on recipe cards - recipe_json['steps'][0]['instruction'] = f'*{parsed_description}* \n\n' + recipe_json['steps'][0]['instruction'] + if len(recipe_json['description']) > 256: # split at 256 as long descriptions don't look good on recipe cards + recipe_json['steps'][0]['instruction'] = f"*{recipe_json['description']}* \n\n" + recipe_json['steps'][0]['instruction'] else: - recipe_json['description'] = parsed_description[:512] + recipe_json['description'] = recipe_json['description'][:512] try: for x in scrape.ingredients(): @@ -191,16 +200,44 @@ def get_from_scraper(scrape, request): except Exception: pass - if recipe_json['source_url']: - automations = Automation.objects.filter(type=Automation.INSTRUCTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').order_by('order').all()[:512] - for a in automations: - if re.match(a.param_1, (recipe_json['source_url'])[:512]): - for s in recipe_json['steps']: - s['instruction'] = re.sub(a.param_2, a.param_3, s['instruction']) + try: + recipe_json['properties'] = get_recipe_properties(request.space, scrape.schema.nutrients()) + print(recipe_json['properties']) + except Exception: + traceback.print_exc() + pass + + for s in recipe_json['steps']: + s['instruction'] = automation_engine.apply_regex_replace_automation(s['instruction'], Automation.INSTRUCTION_REPLACE) + # re.sub(a.param_2, a.param_3, s['instruction']) return recipe_json +def get_recipe_properties(space, property_data): + # {'servingSize': '1', 'calories': '302 kcal', 'proteinContent': '7,66g', 'fatContent': '11,56g', 'carbohydrateContent': '41,33g'} + properties = { + "property-calories": "calories", + "property-carbohydrates": "carbohydrateContent", + "property-proteins": "proteinContent", + "property-fats": "fatContent", + } + recipe_properties = [] + for pt in PropertyType.objects.filter(space=space, open_data_slug__in=list(properties.keys())).all(): + for p in list(properties.keys()): + if pt.open_data_slug == p: + if properties[p] in property_data: + recipe_properties.append({ + 'property_type': { + 'id': pt.id, + 'name': pt.name, + }, + 'property_amount': parse_servings(property_data[properties[p]]) / float(property_data['servingSize']), + }) + + return recipe_properties + + def get_from_youtube_scraper(url, request): """A YouTube Information Scraper.""" kw, created = Keyword.objects.get_or_create(name='YouTube', space=request.space) @@ -222,11 +259,14 @@ def get_from_youtube_scraper(url, request): ] } + # TODO add automation here try: + automation_engine = AutomationEngine(request, source=url) video = YouTube(url=url) - default_recipe_json['name'] = video.title + default_recipe_json['name'] = automation_engine.apply_regex_replace_automation(video.title, Automation.NAME_REPLACE) default_recipe_json['image'] = video.thumbnail_url - default_recipe_json['steps'][0]['instruction'] = video.description + default_recipe_json['steps'][0]['instruction'] = automation_engine.apply_regex_replace_automation(video.description, Automation.INSTRUCTION_REPLACE) + except Exception: pass @@ -234,7 +274,7 @@ def get_from_youtube_scraper(url, request): def parse_name(name): - if type(name) == list: + if isinstance(name, list): try: name = name[0] except Exception: @@ -278,16 +318,16 @@ def parse_instructions(instructions): """ instruction_list = [] - if type(instructions) == list: + if isinstance(instructions, list): for i in instructions: - if type(i) == str: + if isinstance(i, str): instruction_list.append(clean_instruction_string(i)) else: if 'text' in i: instruction_list.append(clean_instruction_string(i['text'])) elif 'itemListElement' in i: for ile in i['itemListElement']: - if type(ile) == str: + if isinstance(ile, str): instruction_list.append(clean_instruction_string(ile)) elif 'text' in ile: instruction_list.append(clean_instruction_string(ile['text'])) @@ -303,13 +343,13 @@ def parse_image(image): # check if list of images is returned, take first if so if not image: return None - if type(image) == list: + if isinstance(image, list): for pic in image: - if (type(pic) == str) and (pic[:4] == 'http'): + if (isinstance(pic, str)) and (pic[:4] == 'http'): image = pic elif 'url' in pic: image = pic['url'] - elif type(image) == dict: + elif isinstance(image, dict): if 'url' in image: image = image['url'] @@ -320,12 +360,12 @@ def parse_image(image): def parse_servings(servings): - if type(servings) == str: + if isinstance(servings, str): try: servings = int(re.search(r'\d+', servings).group()) except AttributeError: servings = 1 - elif type(servings) == list: + elif isinstance(servings, list): try: servings = int(re.findall(r'\b\d+\b', servings[0])[0]) except KeyError: @@ -334,12 +374,12 @@ def parse_servings(servings): def parse_servings_text(servings): - if type(servings) == str: + if isinstance(servings, str): try: - servings = re.sub("\d+", '', servings).strip() + servings = re.sub("\\d+", '', servings).strip() except Exception: servings = '' - if type(servings) == list: + if isinstance(servings, list): try: servings = parse_servings_text(servings[1]) except Exception: @@ -356,7 +396,7 @@ def parse_time(recipe_time): recipe_time = round(iso_parse_duration(recipe_time).seconds / 60) except ISO8601Error: try: - if (type(recipe_time) == list and len(recipe_time) > 0): + if (isinstance(recipe_time, list) and len(recipe_time) > 0): recipe_time = recipe_time[0] recipe_time = round(parse_duration(recipe_time).seconds / 60) except AttributeError: @@ -365,13 +405,18 @@ def parse_time(recipe_time): return recipe_time -def parse_keywords(keyword_json, space): +def parse_keywords(keyword_json, request): keywords = [] + automation_engine = AutomationEngine(request) + # keywords as list for kw in keyword_json: kw = normalize_string(kw) + # if alias exists use that instead + if len(kw) != 0: - if k := Keyword.objects.filter(name=kw, space=space).first(): + automation_engine.apply_keyword_automation(kw) + if k := Keyword.objects.filter(name=kw, space=request.space).first(): keywords.append({'label': str(k), 'name': k.name, 'id': k.id}) else: keywords.append({'label': kw, 'name': kw}) @@ -382,15 +427,15 @@ def parse_keywords(keyword_json, space): def listify_keywords(keyword_list): # keywords as string try: - if type(keyword_list[0]) == dict: + if isinstance(keyword_list[0], dict): return keyword_list except (KeyError, IndexError): pass - if type(keyword_list) == str: + if isinstance(keyword_list, str): keyword_list = keyword_list.split(',') # keywords as string in list - if (type(keyword_list) == list and len(keyword_list) == 1 and ',' in keyword_list[0]): + if (isinstance(keyword_list, list) and len(keyword_list) == 1 and ',' in keyword_list[0]): keyword_list = keyword_list[0].split(',') return [x.strip() for x in keyword_list] @@ -444,13 +489,13 @@ def get_images_from_soup(soup, url): def clean_dict(input_dict, key): - if type(input_dict) == dict: + if isinstance(input_dict, dict): for x in list(input_dict): if x == key: del input_dict[x] - elif type(input_dict[x]) == dict: + elif isinstance(input_dict[x], dict): input_dict[x] = clean_dict(input_dict[x], key) - elif type(input_dict[x]) == list: + elif isinstance(input_dict[x], list): temp_list = [] for e in input_dict[x]: temp_list.append(clean_dict(e, key)) diff --git a/cookbook/helper/scope_middleware.py b/cookbook/helper/scope_middleware.py index a0218f089..8c2613c8f 100644 --- a/cookbook/helper/scope_middleware.py +++ b/cookbook/helper/scope_middleware.py @@ -1,8 +1,6 @@ from django.urls import reverse from django_scopes import scope, scopes_disabled from oauth2_provider.contrib.rest_framework import OAuth2Authentication -from rest_framework.authentication import TokenAuthentication -from rest_framework.authtoken.models import Token from rest_framework.exceptions import AuthenticationFailed from cookbook.views import views @@ -50,7 +48,6 @@ class ScopeMiddleware: return views.no_groups(request) request.space = user_space.space - # with scopes_disabled(): with scope(space=request.space): return self.get_response(request) else: diff --git a/cookbook/helper/shopping_helper.py b/cookbook/helper/shopping_helper.py index c4a8b0796..ffcf5beab 100644 --- a/cookbook/helper/shopping_helper.py +++ b/cookbook/helper/shopping_helper.py @@ -1,16 +1,13 @@ from datetime import timedelta from decimal import Decimal -from django.contrib.postgres.aggregates import ArrayAgg from django.db.models import F, OuterRef, Q, Subquery, Value from django.db.models.functions import Coalesce from django.utils import timezone from django.utils.translation import gettext as _ -from cookbook.helper.HelperFunctions import Round, str2bool from cookbook.models import (Ingredient, MealPlan, Recipe, ShoppingListEntry, ShoppingListRecipe, SupermarketCategoryRelation) -from recipes import settings def shopping_helper(qs, request): @@ -47,7 +44,7 @@ class RecipeShoppingEditor(): self.mealplan = self._kwargs.get('mealplan', None) if type(self.mealplan) in [int, float]: self.mealplan = MealPlan.objects.filter(id=self.mealplan, space=self.space) - if type(self.mealplan) == dict: + if isinstance(self.mealplan, dict): self.mealplan = MealPlan.objects.filter(id=self.mealplan['id'], space=self.space).first() self.id = self._kwargs.get('id', None) @@ -69,11 +66,12 @@ class RecipeShoppingEditor(): @property def _recipe_servings(self): - return getattr(self.recipe, 'servings', None) or getattr(getattr(self.mealplan, 'recipe', None), 'servings', None) or getattr(getattr(self._shopping_list_recipe, 'recipe', None), 'servings', None) + return getattr(self.recipe, 'servings', None) or getattr(getattr(self.mealplan, 'recipe', None), 'servings', + None) or getattr(getattr(self._shopping_list_recipe, 'recipe', None), 'servings', None) @property def _servings_factor(self): - return Decimal(self.servings)/Decimal(self._recipe_servings) + return Decimal(self.servings) / Decimal(self._recipe_servings) @property def _shared_users(self): @@ -90,9 +88,10 @@ class RecipeShoppingEditor(): def get_recipe_ingredients(self, id, exclude_onhand=False): if exclude_onhand: - return Ingredient.objects.filter(step__recipe__id=id, food__ignore_shopping=False, space=self.space).exclude(food__onhand_users__id__in=[x.id for x in self._shared_users]) + return Ingredient.objects.filter(step__recipe__id=id, food__ignore_shopping=False, space=self.space).exclude( + food__onhand_users__id__in=[x.id for x in self._shared_users]) else: - return Ingredient.objects.filter(step__recipe__id=id, food__ignore_shopping=False, space=self.space) + return Ingredient.objects.filter(step__recipe__id=id, food__ignore_shopping=False, space=self.space) @property def _include_related(self): @@ -109,7 +108,7 @@ class RecipeShoppingEditor(): self.servings = float(servings) if mealplan := kwargs.get('mealplan', None): - if type(mealplan) == dict: + if isinstance(mealplan, dict): self.mealplan = MealPlan.objects.filter(id=mealplan['id'], space=self.space).first() else: self.mealplan = mealplan @@ -170,14 +169,14 @@ class RecipeShoppingEditor(): try: self._shopping_list_recipe.delete() return True - except: + except BaseException: return False def _add_ingredients(self, ingredients=None): if not ingredients: return - elif type(ingredients) == list: - ingredients = Ingredient.objects.filter(id__in=ingredients) + elif isinstance(ingredients, list): + ingredients = Ingredient.objects.filter(id__in=ingredients, food__ignore_shopping=False) existing = self._shopping_list_recipe.entries.filter(ingredient__in=ingredients).values_list('ingredient__pk', flat=True) add_ingredients = ingredients.exclude(id__in=existing) @@ -199,120 +198,3 @@ class RecipeShoppingEditor(): to_delete = self._shopping_list_recipe.entries.exclude(ingredient__in=ingredients) ShoppingListEntry.objects.filter(id__in=to_delete).delete() self._shopping_list_recipe = self.get_shopping_list_recipe(self.id, self.created_by, self.space) - - -# # TODO refactor as class -# def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None, append=False): -# """ -# Creates ShoppingListRecipe and associated ShoppingListEntrys from a recipe or a meal plan with a recipe -# :param list_recipe: Modify an existing ShoppingListRecipe -# :param recipe: Recipe to use as list of ingredients. One of [recipe, mealplan] are required -# :param mealplan: alternatively use a mealplan recipe as source of ingredients -# :param servings: Optional: Number of servings to use to scale shoppinglist. If servings = 0 an existing recipe list will be deleted -# :param ingredients: Ingredients, list of ingredient IDs to include on the shopping list. When not provided all ingredients will be used -# :param append: If False will remove any entries not included with ingredients, when True will append ingredients to the shopping list -# """ -# r = recipe or getattr(mealplan, 'recipe', None) or getattr(list_recipe, 'recipe', None) -# if not r: -# raise ValueError(_("You must supply a recipe or mealplan")) - -# created_by = created_by or getattr(ShoppingListEntry.objects.filter(list_recipe=list_recipe).first(), 'created_by', None) -# if not created_by: -# raise ValueError(_("You must supply a created_by")) - -# try: -# servings = float(servings) -# except (ValueError, TypeError): -# servings = getattr(mealplan, 'servings', 1.0) - -# servings_factor = servings / r.servings - -# shared_users = list(created_by.get_shopping_share()) -# shared_users.append(created_by) -# if list_recipe: -# created = False -# else: -# list_recipe = ShoppingListRecipe.objects.create(recipe=r, mealplan=mealplan, servings=servings) -# created = True - -# related_step_ing = [] -# if servings == 0 and not created: -# list_recipe.delete() -# return [] -# elif ingredients: -# ingredients = Ingredient.objects.filter(pk__in=ingredients, space=space) -# else: -# ingredients = Ingredient.objects.filter(step__recipe=r, food__ignore_shopping=False, space=space) - -# if exclude_onhand := created_by.userpreference.mealplan_autoexclude_onhand: -# ingredients = ingredients.exclude(food__onhand_users__id__in=[x.id for x in shared_users]) - -# if related := created_by.userpreference.mealplan_autoinclude_related: -# # TODO: add levels of related recipes (related recipes of related recipes) to use when auto-adding mealplans -# related_recipes = r.get_related_recipes() - -# for x in related_recipes: -# # related recipe is a Step serving size is driven by recipe serving size -# # TODO once/if Steps can have a serving size this needs to be refactored -# if exclude_onhand: -# # if steps are used more than once in a recipe or subrecipe - I don' think this results in the desired behavior -# related_step_ing += Ingredient.objects.filter(step__recipe=x, space=space).exclude(food__onhand_users__id__in=[x.id for x in shared_users]).values_list('id', flat=True) -# else: -# related_step_ing += Ingredient.objects.filter(step__recipe=x, space=space).values_list('id', flat=True) - -# x_ing = [] -# if ingredients.filter(food__recipe=x).exists(): -# for ing in ingredients.filter(food__recipe=x): -# if exclude_onhand: -# x_ing = Ingredient.objects.filter(step__recipe=x, food__ignore_shopping=False, space=space).exclude(food__onhand_users__id__in=[x.id for x in shared_users]) -# else: -# x_ing = Ingredient.objects.filter(step__recipe=x, food__ignore_shopping=False, space=space).exclude(food__ignore_shopping=True) -# for i in [x for x in x_ing]: -# ShoppingListEntry.objects.create( -# list_recipe=list_recipe, -# food=i.food, -# unit=i.unit, -# ingredient=i, -# amount=i.amount * Decimal(servings_factor), -# created_by=created_by, -# space=space, -# ) -# # dont' add food to the shopping list that are actually recipes that will be added as ingredients -# ingredients = ingredients.exclude(food__recipe=x) - -# add_ingredients = list(ingredients.values_list('id', flat=True)) + related_step_ing -# if not append: -# existing_list = ShoppingListEntry.objects.filter(list_recipe=list_recipe) -# # delete shopping list entries not included in ingredients -# existing_list.exclude(ingredient__in=ingredients).delete() -# # add shopping list entries that did not previously exist -# add_ingredients = set(add_ingredients) - set(existing_list.values_list('ingredient__id', flat=True)) -# add_ingredients = Ingredient.objects.filter(id__in=add_ingredients, space=space) - -# # if servings have changed, update the ShoppingListRecipe and existing Entries -# if servings <= 0: -# servings = 1 - -# if not created and list_recipe.servings != servings: -# update_ingredients = set(ingredients.values_list('id', flat=True)) - set(add_ingredients.values_list('id', flat=True)) -# list_recipe.servings = servings -# list_recipe.save() -# for sle in ShoppingListEntry.objects.filter(list_recipe=list_recipe, ingredient__id__in=update_ingredients): -# sle.amount = sle.ingredient.amount * Decimal(servings_factor) -# sle.save() - -# # add any missing Entries -# for i in [x for x in add_ingredients if x.food]: - -# ShoppingListEntry.objects.create( -# list_recipe=list_recipe, -# food=i.food, -# unit=i.unit, -# ingredient=i, -# amount=i.amount * Decimal(servings_factor), -# created_by=created_by, -# space=space, -# ) - -# # return all shopping list items -# return list_recipe \ No newline at end of file diff --git a/cookbook/helper/template_helper.py b/cookbook/helper/template_helper.py index 9bde2fc06..016779a77 100644 --- a/cookbook/helper/template_helper.py +++ b/cookbook/helper/template_helper.py @@ -2,7 +2,6 @@ from gettext import gettext as _ import bleach import markdown as md -from bleach_allowlist import markdown_attrs, markdown_tags from jinja2 import Template, TemplateSyntaxError, UndefinedError from markdown.extensions.tables import TableExtension @@ -53,9 +52,17 @@ class IngredientObject(object): def render_instructions(step): # TODO deduplicate markdown cleanup code instructions = step.instruction - tags = markdown_tags + [ - 'pre', 'table', 'td', 'tr', 'th', 'tbody', 'style', 'thead', 'img' - ] + tags = { + "h1", "h2", "h3", "h4", "h5", "h6", + "b", "i", "strong", "em", "tt", + "p", "br", + "span", "div", "blockquote", "code", "pre", "hr", + "ul", "ol", "li", "dd", "dt", + "img", + "a", + "sub", "sup", + 'pre', 'table', 'td', 'tr', 'th', 'tbody', 'style', 'thead' + } parsed_md = md.markdown( instructions, extensions=[ @@ -63,7 +70,11 @@ def render_instructions(step): # TODO deduplicate markdown cleanup code UrlizeExtension(), MarkdownFormatExtension() ] ) - markdown_attrs['*'] = markdown_attrs['*'] + ['class', 'width', 'height'] + markdown_attrs = { + "*": ["id", "class", 'width', 'height'], + "img": ["src", "alt", "title"], + "a": ["href", "alt", "title"], + } instructions = bleach.clean(parsed_md, tags, markdown_attrs) diff --git a/cookbook/helper/unit_conversion_helper.py b/cookbook/helper/unit_conversion_helper.py new file mode 100644 index 000000000..4b9f6bf0b --- /dev/null +++ b/cookbook/helper/unit_conversion_helper.py @@ -0,0 +1,141 @@ +from django.core.cache import caches +from decimal import Decimal + +from cookbook.helper.cache_helper import CacheHelper +from cookbook.models import Ingredient, Unit + +CONVERSION_TABLE = { + 'weight': { + 'g': 1000, + 'kg': 1, + 'ounce': 35.274, + 'pound': 2.20462 + }, + 'volume': { + 'ml': 1000, + 'l': 1, + 'fluid_ounce': 33.814, + 'pint': 2.11338, + 'quart': 1.05669, + 'gallon': 0.264172, + 'tbsp': 67.628, + 'tsp': 202.884, + 'imperial_fluid_ounce': 35.1951, + 'imperial_pint': 1.75975, + 'imperial_quart': 0.879877, + 'imperial_gallon': 0.219969, + 'imperial_tbsp': 56.3121, + 'imperial_tsp': 168.936, + }, +} + +BASE_UNITS_WEIGHT = list(CONVERSION_TABLE['weight'].keys()) +BASE_UNITS_VOLUME = list(CONVERSION_TABLE['volume'].keys()) + + +class ConversionException(Exception): + pass + + +class UnitConversionHelper: + space = None + + def __init__(self, space): + """ + Initializes unit conversion helper + :param space: space to perform conversions on + """ + self.space = space + + @staticmethod + def convert_from_to(from_unit, to_unit, amount): + """ + Convert from one base unit to another. Throws ConversionException if trying to convert between different systems (weight/volume) or if units are not supported. + :param from_unit: str unit to convert from + :param to_unit: str unit to convert to + :param amount: amount to convert + :return: Decimal converted amount + """ + system = None + if from_unit in BASE_UNITS_WEIGHT and to_unit in BASE_UNITS_WEIGHT: + system = 'weight' + if from_unit in BASE_UNITS_VOLUME and to_unit in BASE_UNITS_VOLUME: + system = 'volume' + + if not system: + raise ConversionException('Trying to convert units not existing or not in one unit system (weight/volume)') + + return Decimal(amount / Decimal(CONVERSION_TABLE[system][from_unit] / CONVERSION_TABLE[system][to_unit])) + + def base_conversions(self, ingredient_list): + """ + Calculates all possible base unit conversions for each ingredient give. + Converts to all common base units IF they exist in the unit database of the space. + For useful results all ingredients passed should be of the same food, otherwise filtering afterwards might be required. + :param ingredient_list: list of ingredients to convert + :return: ingredient list with appended conversions + """ + base_conversion_ingredient_list = ingredient_list.copy() + for i in ingredient_list: + try: + conversion_unit = i.unit.name + if i.unit.base_unit: + conversion_unit = i.unit.base_unit + + # TODO allow setting which units to convert to? possibly only once conversions become visible + units = caches['default'].get(CacheHelper(self.space).BASE_UNITS_CACHE_KEY, None) + if not units: + units = Unit.objects.filter(space=self.space, base_unit__in=(BASE_UNITS_VOLUME + BASE_UNITS_WEIGHT)).all() + caches['default'].set(CacheHelper(self.space).BASE_UNITS_CACHE_KEY, units, 60 * 60) # cache is cleared on unit save signal so long duration is fine + + for u in units: + try: + ingredient = Ingredient(amount=self.convert_from_to(conversion_unit, u.base_unit, i.amount), unit=u, food=ingredient_list[0].food, ) + if not any((x.unit.name == ingredient.unit.name or x.unit.base_unit == ingredient.unit.name) for x in base_conversion_ingredient_list): + base_conversion_ingredient_list.append(ingredient) + except ConversionException: + pass + except Exception: + pass + + return base_conversion_ingredient_list + + def get_conversions(self, ingredient): + """ + Converts an ingredient to all possible conversions based on the custom unit conversion database. + After that passes conversion to UnitConversionHelper.base_conversions() to get all base conversions possible. + :param ingredient: Ingredient object + :return: list of ingredients with all possible custom and base conversions + """ + conversions = [ingredient] + if ingredient.unit: + for c in ingredient.unit.unit_conversion_base_relation.all(): + if c.space == self.space: + r = self._uc_convert(c, ingredient.amount, ingredient.unit, ingredient.food) + if r and r not in conversions: + conversions.append(r) + for c in ingredient.unit.unit_conversion_converted_relation.all(): + if c.space == self.space: + r = self._uc_convert(c, ingredient.amount, ingredient.unit, ingredient.food) + if r and r not in conversions: + conversions.append(r) + + conversions = self.base_conversions(conversions) + + return conversions + + def _uc_convert(self, uc, amount, unit, food): + """ + Helper to calculate values for custom unit conversions. + Converts given base values using the passed UnitConversion object into a converted Ingredient + :param uc: UnitConversion object + :param amount: base amount + :param unit: base unit + :param food: base food + :return: converted ingredient object from base amount/unit/food + """ + if uc.food is None or uc.food == food: + if unit == uc.base_unit: + return Ingredient(amount=amount * (uc.converted_amount / uc.base_amount), unit=uc.converted_unit, food=food, space=self.space) + else: + return Ingredient(amount=amount * (uc.base_amount / uc.converted_amount), unit=uc.base_unit, food=food, space=self.space) diff --git a/cookbook/integration/cheftap.py b/cookbook/integration/cheftap.py index cf462d9a3..2979f013f 100644 --- a/cookbook/integration/cheftap.py +++ b/cookbook/integration/cheftap.py @@ -36,7 +36,7 @@ class ChefTap(Integration): recipe = Recipe.objects.create(name=title, created_by=self.request.user, internal=True, space=self.request.space, ) - step = Step.objects.create(instruction='\n'.join(directions), space=self.request.space,) + step = Step.objects.create(instruction='\n'.join(directions), space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,) if source_url != '': step.instruction += '\n' + source_url diff --git a/cookbook/integration/chowdown.py b/cookbook/integration/chowdown.py index 2f635436d..3d41cf481 100644 --- a/cookbook/integration/chowdown.py +++ b/cookbook/integration/chowdown.py @@ -55,7 +55,7 @@ class Chowdown(Integration): recipe.keywords.add(keyword) step = Step.objects.create( - instruction='\n'.join(directions) + '\n\n' + '\n'.join(descriptions), space=self.request.space, + instruction='\n'.join(directions) + '\n\n' + '\n'.join(descriptions), space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) diff --git a/cookbook/integration/cookbookapp.py b/cookbook/integration/cookbookapp.py index c35de64f2..21d9d7f30 100644 --- a/cookbook/integration/cookbookapp.py +++ b/cookbook/integration/cookbookapp.py @@ -1,20 +1,15 @@ -import base64 -import gzip -import json import re -from gettext import gettext as _ from io import BytesIO import requests import validators -import yaml from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.recipe_url_import import (get_from_scraper, get_images_from_soup, iso_duration_to_minutes) from cookbook.helper.scrapers.scrapers import text_scraper from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Keyword, Recipe, Step +from cookbook.models import Ingredient, Recipe, Step class CookBookApp(Integration): @@ -25,7 +20,6 @@ class CookBookApp(Integration): def get_recipe_from_file(self, file): recipe_html = file.getvalue().decode("utf-8") - # recipe_json, recipe_tree, html_data, images = get_recipe_from_source(recipe_html, 'CookBookApp', self.request) scrape = text_scraper(text=recipe_html) recipe_json = get_from_scraper(scrape, self.request) images = list(dict.fromkeys(get_images_from_soup(scrape.soup, None))) @@ -37,7 +31,7 @@ class CookBookApp(Integration): try: recipe.servings = re.findall('([0-9])+', recipe_json['recipeYield'])[0] - except Exception as e: + except Exception: pass try: @@ -47,7 +41,8 @@ class CookBookApp(Integration): pass # assuming import files only contain single step - step = Step.objects.create(instruction=recipe_json['steps'][0]['instruction'], space=self.request.space, ) + step = Step.objects.create(instruction=recipe_json['steps'][0]['instruction'], space=self.request.space, + show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) if 'nutrition' in recipe_json: step.instruction = step.instruction + '\n\n' + recipe_json['nutrition'] @@ -62,7 +57,7 @@ class CookBookApp(Integration): if unit := ingredient.get('unit', None): u = ingredient_parser.get_unit(unit.get('name', None)) step.ingredients.add(Ingredient.objects.create( - food=f, unit=u, amount=ingredient.get('amount', None), note=ingredient.get('note', None), original_text=ingredient.get('original_text', None), space=self.request.space, + food=f, unit=u, amount=ingredient.get('amount', None), note=ingredient.get('note', None), original_text=ingredient.get('original_text', None), space=self.request.space, )) if len(images) > 0: diff --git a/cookbook/integration/cookmate.py b/cookbook/integration/cookmate.py index 940952ce5..a51ca45b9 100644 --- a/cookbook/integration/cookmate.py +++ b/cookbook/integration/cookmate.py @@ -1,17 +1,12 @@ -import base64 -import json from io import BytesIO -from gettext import gettext as _ - import requests import validators -from lxml import etree from cookbook.helper.ingredient_parser import IngredientParser -from cookbook.helper.recipe_url_import import parse_servings, parse_time, parse_servings_text +from cookbook.helper.recipe_url_import import parse_servings, parse_servings_text, parse_time from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Keyword, Recipe, Step +from cookbook.models import Ingredient, Recipe, Step class Cookmate(Integration): @@ -50,7 +45,7 @@ class Cookmate(Integration): for step in recipe_text.getchildren(): if step.text: step = Step.objects.create( - instruction=step.text.strip(), space=self.request.space, + instruction=step.text.strip(), space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) recipe.steps.add(step) diff --git a/cookbook/integration/copymethat.py b/cookbook/integration/copymethat.py index 421c967d7..01d51c844 100644 --- a/cookbook/integration/copymethat.py +++ b/cookbook/integration/copymethat.py @@ -1,4 +1,3 @@ -import re from io import BytesIO from zipfile import ZipFile @@ -26,12 +25,13 @@ class CopyMeThat(Integration): except AttributeError: source = None - recipe = Recipe.objects.create(name=file.find("div", {"id": "name"}).text.strip()[:128], source_url=source, created_by=self.request.user, internal=True, space=self.request.space, ) + recipe = Recipe.objects.create(name=file.find("div", {"id": "name"}).text.strip( + )[:128], source_url=source, created_by=self.request.user, internal=True, space=self.request.space, ) for category in file.find_all("span", {"class": "recipeCategory"}): keyword, created = Keyword.objects.get_or_create(name=category.text, space=self.request.space) recipe.keywords.add(keyword) - + try: recipe.servings = parse_servings(file.find("a", {"id": "recipeYield"}).text.strip()) recipe.working_time = iso_duration_to_minutes(file.find("span", {"meta": "prepTime"}).text.strip()) @@ -51,7 +51,7 @@ class CopyMeThat(Integration): except AttributeError: pass - step = Step.objects.create(instruction='', space=self.request.space, ) + step = Step.objects.create(instruction='', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) @@ -61,7 +61,14 @@ class CopyMeThat(Integration): if not isinstance(ingredient, Tag) or not ingredient.text.strip() or "recipeIngredient_spacer" in ingredient['class']: continue if any(x in ingredient['class'] for x in ["recipeIngredient_subheader", "recipeIngredient_note"]): - step.ingredients.add(Ingredient.objects.create(is_header=True, note=ingredient.text.strip()[:256], original_text=ingredient.text.strip(), space=self.request.space, )) + step.ingredients.add( + Ingredient.objects.create( + is_header=True, + note=ingredient.text.strip()[ + :256], + original_text=ingredient.text.strip(), + space=self.request.space, + )) else: amount, unit, food, note = ingredient_parser.parse(ingredient.text.strip()) f = ingredient_parser.get_food(food) @@ -78,7 +85,7 @@ class CopyMeThat(Integration): step.save() recipe.steps.add(step) step = Step.objects.create(instruction='', space=self.request.space, ) - + step.name = instruction.text.strip()[:128] else: step.instruction += instruction.text.strip() + ' \n\n' diff --git a/cookbook/integration/default.py b/cookbook/integration/default.py index 951a5312c..00a157966 100644 --- a/cookbook/integration/default.py +++ b/cookbook/integration/default.py @@ -1,4 +1,5 @@ import json +import traceback from io import BytesIO, StringIO from re import match from zipfile import ZipFile @@ -19,7 +20,10 @@ class Default(Integration): recipe = self.decode_recipe(recipe_string) images = list(filter(lambda v: match('image.*', v), recipe_zip.namelist())) if images: - self.import_recipe_image(recipe, BytesIO(recipe_zip.read(images[0])), filetype=get_filetype(images[0])) + try: + self.import_recipe_image(recipe, BytesIO(recipe_zip.read(images[0])), filetype=get_filetype(images[0])) + except AttributeError: + traceback.print_exc() return recipe def decode_recipe(self, string): @@ -54,7 +58,7 @@ class Default(Integration): try: recipe_zip_obj.writestr(f'image{get_filetype(r.image.file.name)}', r.image.file.read()) - except ValueError: + except (ValueError, FileNotFoundError): pass recipe_zip_obj.close() @@ -67,4 +71,4 @@ class Default(Integration): export_zip_obj.close() - return [[ self.get_export_file_name(), export_zip_stream.getvalue() ]] \ No newline at end of file + return [[self.get_export_file_name(), export_zip_stream.getvalue()]] diff --git a/cookbook/integration/domestica.py b/cookbook/integration/domestica.py index d1cc3bc19..dd0b36493 100644 --- a/cookbook/integration/domestica.py +++ b/cookbook/integration/domestica.py @@ -28,7 +28,7 @@ class Domestica(Integration): recipe.save() step = Step.objects.create( - instruction=file['directions'], space=self.request.space, + instruction=file['directions'], space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) if file['source'] != '': diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index dbe3d6678..7386b50d9 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -1,4 +1,3 @@ -import traceback import datetime import traceback import uuid @@ -18,8 +17,7 @@ from lxml import etree from cookbook.helper.image_processing import handle_image from cookbook.models import Keyword, Recipe -from recipes.settings import DEBUG -from recipes.settings import EXPORT_FILE_CACHE_DURATION +from recipes.settings import DEBUG, EXPORT_FILE_CACHE_DURATION class Integration: @@ -39,7 +37,6 @@ class Integration: self.ignored_recipes = [] description = f'Imported by {request.user.get_user_display_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}' - icon = '📥' try: last_kw = Keyword.objects.filter(name__regex=r'^(Import [0-9]+)', space=request.space).latest('created_at') @@ -52,23 +49,19 @@ class Integration: self.keyword = parent.add_child( name=name, description=description, - icon=icon, space=request.space ) except (IntegrityError, ValueError): # in case, for whatever reason, the name does exist append UUID to it. Not nice but works for now. self.keyword = parent.add_child( name=f'{name} {str(uuid.uuid4())[0:8]}', description=description, - icon=icon, space=request.space ) - - def do_export(self, recipes, el): with scope(space=self.request.space): - el.total_recipes = len(recipes) + el.total_recipes = len(recipes) el.cache_duration = EXPORT_FILE_CACHE_DURATION el.save() @@ -80,7 +73,7 @@ class Integration: export_file = file else: - #zip the files if there is more then one file + # zip the files if there is more then one file export_filename = self.get_export_file_name() export_stream = BytesIO() export_obj = ZipFile(export_stream, 'w') @@ -91,8 +84,7 @@ class Integration: export_obj.close() export_file = export_stream.getvalue() - - cache.set('export_file_'+str(el.pk), {'filename': export_filename, 'file': export_file}, EXPORT_FILE_CACHE_DURATION) + cache.set('export_file_' + str(el.pk), {'filename': export_filename, 'file': export_file}, EXPORT_FILE_CACHE_DURATION) el.running = False el.save() @@ -100,7 +92,6 @@ class Integration: response['Content-Disposition'] = 'attachment; filename="' + export_filename + '"' return response - def import_file_name_filter(self, zip_info_object): """ Since zipfile.namelist() returns all files in all subdirectories this function allows filtering of files @@ -164,7 +155,7 @@ class Integration: for z in file_list: try: - if not hasattr(z, 'filename') or type(z) == Tag: + if not hasattr(z, 'filename') or isinstance(z, Tag): recipe = self.get_recipe_from_file(z) else: recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename))) @@ -298,7 +289,6 @@ class Integration: if DEBUG: traceback.print_exc() - def get_export_file_name(self, format='zip'): return "export_{}.{}".format(datetime.datetime.now().strftime("%Y-%m-%d"), format) diff --git a/cookbook/integration/mealie.py b/cookbook/integration/mealie.py index ccd16ce93..5e4e1578d 100644 --- a/cookbook/integration/mealie.py +++ b/cookbook/integration/mealie.py @@ -25,7 +25,7 @@ class Mealie(Integration): created_by=self.request.user, internal=True, space=self.request.space) for s in recipe_json['recipe_instructions']: - step = Step.objects.create(instruction=s['text'], space=self.request.space, ) + step = Step.objects.create(instruction=s['text'], space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) recipe.steps.add(step) step = recipe.steps.first() diff --git a/cookbook/integration/mealmaster.py b/cookbook/integration/mealmaster.py index dcd70efa2..9dce4fdd9 100644 --- a/cookbook/integration/mealmaster.py +++ b/cookbook/integration/mealmaster.py @@ -39,7 +39,7 @@ class MealMaster(Integration): recipe.keywords.add(keyword) step = Step.objects.create( - instruction='\n'.join(directions) + '\n\n', space=self.request.space, + instruction='\n'.join(directions) + '\n\n', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) diff --git a/cookbook/integration/melarecipes.py b/cookbook/integration/melarecipes.py index 88ff4d355..9679a90f1 100644 --- a/cookbook/integration/melarecipes.py +++ b/cookbook/integration/melarecipes.py @@ -67,7 +67,7 @@ class MelaRecipes(Integration): f = ingredient_parser.get_food(food) u = ingredient_parser.get_unit(unit) step.ingredients.add(Ingredient.objects.create( - food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space, + food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, )) recipe.steps.add(step) diff --git a/cookbook/integration/nextcloud_cookbook.py b/cookbook/integration/nextcloud_cookbook.py index baa8625ce..4357c509b 100644 --- a/cookbook/integration/nextcloud_cookbook.py +++ b/cookbook/integration/nextcloud_cookbook.py @@ -2,13 +2,14 @@ import json import re from io import BytesIO, StringIO from zipfile import ZipFile + from PIL import Image from cookbook.helper.image_processing import get_filetype from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.recipe_url_import import iso_duration_to_minutes from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Keyword, Recipe, Step, NutritionInformation +from cookbook.models import Ingredient, Keyword, NutritionInformation, Recipe, Step class NextcloudCookbook(Integration): @@ -51,9 +52,14 @@ class NextcloudCookbook(Integration): ingredients_added = False for s in recipe_json['recipeInstructions']: - step = Step.objects.create( - instruction=s, space=self.request.space, - ) + if 'text' in s: + step = Step.objects.create( + instruction=s['text'], name=s['name'], space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, + ) + else: + step = Step.objects.create( + instruction=s, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, + ) if not ingredients_added: if len(recipe_json['description'].strip()) > 500: step.instruction = recipe_json['description'].strip() + '\n\n' + step.instruction @@ -85,7 +91,7 @@ class NextcloudCookbook(Integration): if nutrition != {}: recipe.nutrition = NutritionInformation.objects.create(**nutrition, space=self.request.space) recipe.save() - except Exception as e: + except Exception: pass for f in self.files: @@ -98,11 +104,10 @@ class NextcloudCookbook(Integration): return recipe def formatTime(self, min): - h = min//60 + h = min // 60 m = min % 60 return f'PT{h}H{m}M0S' - def get_file_from_recipe(self, recipe): export = {} @@ -111,7 +116,7 @@ class NextcloudCookbook(Integration): export['url'] = recipe.source_url export['prepTime'] = self.formatTime(recipe.working_time) export['cookTime'] = self.formatTime(recipe.waiting_time) - export['totalTime'] = self.formatTime(recipe.working_time+recipe.waiting_time) + export['totalTime'] = self.formatTime(recipe.working_time + recipe.waiting_time) export['recipeYield'] = recipe.servings export['image'] = f'/Recipes/{recipe.name}/full.jpg' export['imageUrl'] = f'/Recipes/{recipe.name}/full.jpg' @@ -133,7 +138,6 @@ class NextcloudCookbook(Integration): export['recipeIngredient'] = recipeIngredient export['recipeInstructions'] = recipeInstructions - return "recipe.json", json.dumps(export) def get_files_from_recipes(self, recipes, el, cookie): @@ -163,7 +167,7 @@ class NextcloudCookbook(Integration): export_zip_obj.close() - return [[ self.get_export_file_name(), export_zip_stream.getvalue() ]] + return [[self.get_export_file_name(), export_zip_stream.getvalue()]] def getJPEG(self, imageByte): image = Image.open(BytesIO(imageByte)) @@ -172,14 +176,14 @@ class NextcloudCookbook(Integration): bytes = BytesIO() image.save(bytes, "JPEG") return bytes.getvalue() - + def getThumb(self, size, imageByte): image = Image.open(BytesIO(imageByte)) w, h = image.size - m = min(w, h) + m = min(w, h) - image = image.crop(((w-m)//2, (h-m)//2, (w+m)//2, (h+m)//2)) + image = image.crop(((w - m) // 2, (h - m) // 2, (w + m) // 2, (h + m) // 2)) image = image.resize([size, size], Image.Resampling.LANCZOS) image = image.convert('RGB') diff --git a/cookbook/integration/openeats.py b/cookbook/integration/openeats.py index 9188ca8de..2a30b73c3 100644 --- a/cookbook/integration/openeats.py +++ b/cookbook/integration/openeats.py @@ -1,9 +1,11 @@ import json +from django.utils.translation import gettext as _ + from cookbook.helper.ingredient_parser import IngredientParser from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Recipe, Step, Keyword, Comment, CookLog -from django.utils.translation import gettext as _ +from cookbook.models import Comment, CookLog, Ingredient, Keyword, Recipe, Step + class OpenEats(Integration): @@ -25,16 +27,16 @@ class OpenEats(Integration): if file["source"] != '': instructions += '\n' + _('Recipe source:') + f'[{file["source"]}]({file["source"]})' - cuisine_keyword, created = Keyword.objects.get_or_create(name="Cuisine", space=self.request.space) + cuisine_keyword, created = Keyword.objects.get_or_create(name="Cuisine", space=self.request.space) if file["cuisine"] != '': - keyword, created = Keyword.objects.get_or_create(name=file["cuisine"].strip(), space=self.request.space) + keyword, created = Keyword.objects.get_or_create(name=file["cuisine"].strip(), space=self.request.space) if created: keyword.move(cuisine_keyword, pos="last-child") recipe.keywords.add(keyword) - course_keyword, created = Keyword.objects.get_or_create(name="Course", space=self.request.space) + course_keyword, created = Keyword.objects.get_or_create(name="Course", space=self.request.space) if file["course"] != '': - keyword, created = Keyword.objects.get_or_create(name=file["course"].strip(), space=self.request.space) + keyword, created = Keyword.objects.get_or_create(name=file["course"].strip(), space=self.request.space) if created: keyword.move(course_keyword, pos="last-child") recipe.keywords.add(keyword) @@ -51,7 +53,7 @@ class OpenEats(Integration): recipe.image = f'recipes/openeats-import/{file["photo"]}' recipe.save() - step = Step.objects.create(instruction=instructions, space=self.request.space,) + step = Step.objects.create(instruction=instructions, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,) ingredient_parser = IngredientParser(self.request, True) for ingredient in file['ingredients']: diff --git a/cookbook/integration/paprika.py b/cookbook/integration/paprika.py index 3d78b9eb0..b830d85fd 100644 --- a/cookbook/integration/paprika.py +++ b/cookbook/integration/paprika.py @@ -58,7 +58,7 @@ class Paprika(Integration): pass step = Step.objects.create( - instruction=instructions, space=self.request.space, + instruction=instructions, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) if 'description' in recipe_json and len(recipe_json['description'].strip()) > 500: @@ -90,7 +90,7 @@ class Paprika(Integration): if validators.url(url, public=True): response = requests.get(url) self.import_recipe_image(recipe, BytesIO(response.content)) - except: + except Exception: if recipe_json.get("photo_data", None): self.import_recipe_image(recipe, BytesIO(base64.b64decode(recipe_json['photo_data'])), filetype='.jpeg') diff --git a/cookbook/integration/pdfexport.py b/cookbook/integration/pdfexport.py index fca782473..4fea85781 100644 --- a/cookbook/integration/pdfexport.py +++ b/cookbook/integration/pdfexport.py @@ -1,21 +1,11 @@ -import json -from io import BytesIO -from re import match -from zipfile import ZipFile import asyncio -from pyppeteer import launch - -from rest_framework.renderers import JSONRenderer - -from cookbook.helper.image_processing import get_filetype -from cookbook.integration.integration import Integration -from cookbook.serializer import RecipeExportSerializer - -from cookbook.models import ExportLog -from asgiref.sync import sync_to_async import django.core.management.commands.runserver as runserver -import logging +from asgiref.sync import sync_to_async +from pyppeteer import launch + +from cookbook.integration.integration import Integration + class PDFexport(Integration): @@ -42,7 +32,6 @@ class PDFexport(Integration): } } - files = [] for recipe in recipes: @@ -50,20 +39,18 @@ class PDFexport(Integration): await page.emulateMedia('print') await page.setCookie(cookies) - await page.goto('http://'+cmd.default_addr+':'+cmd.default_port+'/view/recipe/'+str(recipe.id), {'waitUntil': 'domcontentloaded'}) - await page.waitForSelector('#printReady'); + await page.goto('http://' + cmd.default_addr + ':' + cmd.default_port + '/view/recipe/' + str(recipe.id), {'waitUntil': 'domcontentloaded'}) + await page.waitForSelector('#printReady') files.append([recipe.name + '.pdf', await page.pdf(options)]) - await page.close(); + await page.close() el.exported_recipes += 1 el.msg += self.get_recipe_processed_msg(recipe) await sync_to_async(el.save, thread_sensitive=True)() - await browser.close() return files - def get_files_from_recipes(self, recipes, el, cookie): return asyncio.run(self.get_files_from_recipes_async(recipes, el, cookie)) diff --git a/cookbook/integration/pepperplate.py b/cookbook/integration/pepperplate.py index 7cfafa84d..570d702c6 100644 --- a/cookbook/integration/pepperplate.py +++ b/cookbook/integration/pepperplate.py @@ -35,7 +35,7 @@ class Pepperplate(Integration): recipe = Recipe.objects.create(name=title, description=description, created_by=self.request.user, internal=True, space=self.request.space) step = Step.objects.create( - instruction='\n'.join(directions) + '\n\n', space=self.request.space, + instruction='\n'.join(directions) + '\n\n', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) diff --git a/cookbook/integration/plantoeat.py b/cookbook/integration/plantoeat.py index 8eb4cb0dc..c4cc0f135 100644 --- a/cookbook/integration/plantoeat.py +++ b/cookbook/integration/plantoeat.py @@ -1,6 +1,7 @@ from io import BytesIO import requests +import validators from cookbook.helper.ingredient_parser import IngredientParser from cookbook.integration.integration import Integration @@ -45,7 +46,7 @@ class Plantoeat(Integration): recipe = Recipe.objects.create(name=title, description=description, created_by=self.request.user, internal=True, space=self.request.space) step = Step.objects.create( - instruction='\n'.join(directions) + '\n\n', space=self.request.space, + instruction='\n'.join(directions) + '\n\n', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) if tags: @@ -67,8 +68,9 @@ class Plantoeat(Integration): if image_url: try: - response = requests.get(image_url) - self.import_recipe_image(recipe, BytesIO(response.content)) + if validators.url(image_url, public=True): + response = requests.get(image_url) + self.import_recipe_image(recipe, BytesIO(response.content)) except Exception as e: print('failed to import image ', str(e)) diff --git a/cookbook/integration/recettetek.py b/cookbook/integration/recettetek.py index d92fa18c7..87e145ffc 100644 --- a/cookbook/integration/recettetek.py +++ b/cookbook/integration/recettetek.py @@ -46,7 +46,7 @@ class RecetteTek(Integration): if not instructions: instructions = '' - step = Step.objects.create(instruction=instructions, space=self.request.space,) + step = Step.objects.create(instruction=instructions, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients,) # Append the original import url to the step (if it exists) try: diff --git a/cookbook/integration/recipekeeper.py b/cookbook/integration/recipekeeper.py index cd783bfa7..e4f1ee8e4 100644 --- a/cookbook/integration/recipekeeper.py +++ b/cookbook/integration/recipekeeper.py @@ -41,7 +41,7 @@ class RecipeKeeper(Integration): except AttributeError: pass - step = Step.objects.create(instruction='', space=self.request.space, ) + step = Step.objects.create(instruction='', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) for ingredient in file.find("div", {"itemprop": "recipeIngredients"}).findChildren("p"): diff --git a/cookbook/integration/recipesage.py b/cookbook/integration/recipesage.py index 149c1c5dc..70b8838ce 100644 --- a/cookbook/integration/recipesage.py +++ b/cookbook/integration/recipesage.py @@ -5,6 +5,7 @@ import requests import validators from cookbook.helper.ingredient_parser import IngredientParser +from cookbook.helper.recipe_url_import import parse_servings, parse_servings_text, parse_time from cookbook.integration.integration import Integration from cookbook.models import Ingredient, Recipe, Step @@ -18,19 +19,21 @@ class RecipeSage(Integration): created_by=self.request.user, internal=True, space=self.request.space) + if file['recipeYield'] != '': + recipe.servings = parse_servings(file['recipeYield']) + recipe.servings_text = parse_servings_text(file['recipeYield']) + try: - if file['recipeYield'] != '': - recipe.servings = int(file['recipeYield']) + if 'totalTime' in file and file['totalTime'] != '': + recipe.working_time = parse_time(file['totalTime']) - if file['totalTime'] != '': - recipe.waiting_time = int(file['totalTime']) - int(file['timePrep']) - - if file['prepTime'] != '': - recipe.working_time = int(file['timePrep']) - - recipe.save() + if 'timePrep' in file and file['prepTime'] != '': + recipe.working_time = parse_time(file['timePrep']) + recipe.waiting_time = parse_time(file['totalTime']) - parse_time(file['timePrep']) except Exception as e: - print('failed to parse yield or time ', str(e)) + print('failed to parse time ', str(e)) + + recipe.save() ingredient_parser = IngredientParser(self.request, True) ingredients_added = False @@ -46,7 +49,7 @@ class RecipeSage(Integration): f = ingredient_parser.get_food(food) u = ingredient_parser.get_unit(unit) step.ingredients.add(Ingredient.objects.create( - food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space, + food=f, unit=u, amount=amount, note=note, original_text=ingredient, space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, )) recipe.steps.add(step) diff --git a/cookbook/integration/rezeptsuitede.py b/cookbook/integration/rezeptsuitede.py index 75682e1f6..afe3e543c 100644 --- a/cookbook/integration/rezeptsuitede.py +++ b/cookbook/integration/rezeptsuitede.py @@ -2,12 +2,10 @@ import base64 from io import BytesIO from xml import etree -from lxml import etree - from cookbook.helper.ingredient_parser import IngredientParser -from cookbook.helper.recipe_url_import import parse_time, parse_servings, parse_servings_text +from cookbook.helper.recipe_url_import import parse_servings, parse_servings_text from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Recipe, Step, Keyword +from cookbook.models import Ingredient, Keyword, Recipe, Step class Rezeptsuitede(Integration): @@ -22,9 +20,12 @@ class Rezeptsuitede(Integration): name=recipe_xml.find('head').attrib['title'].strip(), created_by=self.request.user, internal=True, space=self.request.space) - if recipe_xml.find('head').attrib['servingtype']: - recipe.servings = parse_servings(recipe_xml.find('head').attrib['servingtype'].strip()) - recipe.servings_text = parse_servings_text(recipe_xml.find('head').attrib['servingtype'].strip()) + try: + if recipe_xml.find('head').attrib['servingtype']: + recipe.servings = parse_servings(recipe_xml.find('head').attrib['servingtype'].strip()) + recipe.servings_text = parse_servings_text(recipe_xml.find('head').attrib['servingtype'].strip()) + except KeyError: + pass if recipe_xml.find('remark') is not None: # description is a list of
  • 's with text if recipe_xml.find('remark').find('line') is not None: @@ -34,7 +35,7 @@ class Rezeptsuitede(Integration): try: if prep.find('step').text: step = Step.objects.create( - instruction=prep.find('step').text.strip(), space=self.request.space, + instruction=prep.find('step').text.strip(), space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) recipe.steps.add(step) except Exception: @@ -50,20 +51,22 @@ class Rezeptsuitede(Integration): for ingredient in recipe_xml.find('part').findall('ingredient'): f = ingredient_parser.get_food(ingredient.attrib['item']) u = ingredient_parser.get_unit(ingredient.attrib['unit']) - amount, unit, note = ingredient_parser.parse_amount(ingredient.attrib['qty']) + amount = 0 + if ingredient.attrib['qty'].strip() != '': + amount, unit, note = ingredient_parser.parse_amount(ingredient.attrib['qty']) ingredient_step.ingredients.add(Ingredient.objects.create(food=f, unit=u, amount=amount, space=self.request.space, )) try: k, created = Keyword.objects.get_or_create(name=recipe_xml.find('head').find('cat').text.strip(), space=self.request.space) recipe.keywords.add(k) - except Exception as e: + except Exception: pass recipe.save() try: self.import_recipe_image(recipe, BytesIO(base64.b64decode(recipe_xml.find('head').find('picbin').text)), filetype='.jpeg') - except: + except BaseException: pass return recipe diff --git a/cookbook/integration/rezkonv.py b/cookbook/integration/rezkonv.py index c0bfcc8ef..d8417ded4 100644 --- a/cookbook/integration/rezkonv.py +++ b/cookbook/integration/rezkonv.py @@ -38,7 +38,7 @@ class RezKonv(Integration): recipe.keywords.add(keyword) step = Step.objects.create( - instruction=' \n'.join(directions) + '\n\n', space=self.request.space, + instruction=' \n'.join(directions) + '\n\n', space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) @@ -60,8 +60,8 @@ class RezKonv(Integration): def split_recipe_file(self, file): recipe_list = [] current_recipe = '' - encoding_list = ['windows-1250', - 'latin-1'] # TODO build algorithm to try trough encodings and fail if none work, use for all importers + # TODO build algorithm to try trough encodings and fail if none work, use for all importers + # encoding_list = ['windows-1250', 'latin-1'] encoding = 'windows-1250' for fl in file.readlines(): try: diff --git a/cookbook/integration/saffron.py b/cookbook/integration/saffron.py index a7ec34f10..ab0b3155c 100644 --- a/cookbook/integration/saffron.py +++ b/cookbook/integration/saffron.py @@ -43,7 +43,7 @@ class Saffron(Integration): recipe = Recipe.objects.create(name=title, description=description, created_by=self.request.user, internal=True, space=self.request.space, ) - step = Step.objects.create(instruction='\n'.join(directions), space=self.request.space, ) + step = Step.objects.create(instruction='\n'.join(directions), space=self.request.space, show_ingredients_table=self.request.user.userpreference.show_step_ingredients, ) ingredient_parser = IngredientParser(self.request, True) for ingredient in ingredients: @@ -59,11 +59,11 @@ class Saffron(Integration): def get_file_from_recipe(self, recipe): - data = "Title: "+recipe.name if recipe.name else ""+"\n" - data += "Description: "+recipe.description if recipe.description else ""+"\n" + data = "Title: " + recipe.name if recipe.name else "" + "\n" + data += "Description: " + recipe.description if recipe.description else "" + "\n" data += "Source: \n" data += "Original URL: \n" - data += "Yield: "+str(recipe.servings)+"\n" + data += "Yield: " + str(recipe.servings) + "\n" data += "Cookbook: \n" data += "Section: \n" data += "Image: \n" @@ -78,13 +78,13 @@ class Saffron(Integration): data += "Ingredients: \n" for ingredient in recipeIngredient: - data += ingredient+"\n" + data += ingredient + "\n" data += "Instructions: \n" for instruction in recipeInstructions: - data += instruction+"\n" + data += instruction + "\n" - return recipe.name+'.txt', data + return recipe.name + '.txt', data def get_files_from_recipes(self, recipes, el, cookie): files = [] diff --git a/cookbook/locale/bg/LC_MESSAGES/django.mo b/cookbook/locale/bg/LC_MESSAGES/django.mo index 2b36ba377..eeb679446 100644 Binary files a/cookbook/locale/bg/LC_MESSAGES/django.mo and b/cookbook/locale/bg/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/bg/LC_MESSAGES/django.po b/cookbook/locale/bg/LC_MESSAGES/django.po index 8d8bf04f9..37e359a09 100644 --- a/cookbook/locale/bg/LC_MESSAGES/django.po +++ b/cookbook/locale/bg/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-04-29 18:42+0200\n" -"PO-Revision-Date: 2022-05-10 15:32+0000\n" -"Last-Translator: zeon \n" +"PO-Revision-Date: 2023-04-12 11:55+0000\n" +"Last-Translator: noxonad \n" "Language-Team: Bulgarian \n" "Language: bg\n" @@ -17,7 +17,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.10.1\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\forms\ingredients.html:34 #: .\cookbook\templates\space.html:49 .\cookbook\templates\stats.html:28 @@ -1433,7 +1433,7 @@ msgstr "" #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "Търсете рецепта..." +msgstr "Търсете рецепта ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" @@ -1818,7 +1818,7 @@ msgid "" msgstr "" " \n" " Пълнотекстови търсения се опитват да нормализират предоставените " -"думи, за да съответстват на често срещани варианти. Например: 'вили, " +"думи, за да съответстват на често срещани варианти. Например: 'вили, " "'вилица', 'вилици' всички ще се нормализират до 'вилиц'.\n" " Има няколко налични метода, описани по-долу, които ще " "контролират как поведението при търсене трябва да реагира, когато се търсят " diff --git a/cookbook/locale/ca/LC_MESSAGES/django.mo b/cookbook/locale/ca/LC_MESSAGES/django.mo index 39e330aa4..995e82180 100644 Binary files a/cookbook/locale/ca/LC_MESSAGES/django.mo and b/cookbook/locale/ca/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/ca/LC_MESSAGES/django.po b/cookbook/locale/ca/LC_MESSAGES/django.po index e7f74547f..a3ba5fc87 100644 --- a/cookbook/locale/ca/LC_MESSAGES/django.po +++ b/cookbook/locale/ca/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2022-05-22 11:20+0000\n" -"Last-Translator: Ramon Aixa Juan \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-07-06 21:19+0000\n" +"Last-Translator: Rubens \n" "Language-Team: Catalan \n" "Language: ca\n" @@ -22,7 +22,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.10.1\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\forms.py:52 msgid "Default unit" @@ -64,7 +64,7 @@ msgstr "Decimals Ingredients" msgid "Shopping list auto sync period" msgstr "Auto-sincronització Llista compra" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Comentaris" @@ -116,7 +116,7 @@ msgstr "Nombre de decimals dels ingredients." msgid "If you want to be able to create and see comments underneath recipes." msgstr "Si vols poder crear i veure comentaris a sota de les receptes." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -133,7 +133,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Barra de navegació s'enganxi a la part superior de la pàgina." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" "Afegeix automàticament els ingredients del pla d'àpats a la llista de la " @@ -155,11 +155,11 @@ msgstr "" "Tots dos camps són opcionals. Si no se'n dóna cap, es mostrarà el nom " "d'usuari" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Nom" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Paraules clau" @@ -171,7 +171,7 @@ msgstr "Temps de preparació en minuts" msgid "Waiting time (cooking/baking) in minutes" msgstr "Temps d'espera (cocció/fornejat) en minuts" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Ruta" @@ -179,11 +179,11 @@ msgstr "Ruta" msgid "Storage UID" msgstr "UID Emmagatzematge" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Per defecte" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -191,21 +191,21 @@ msgstr "" "Per evitar duplicats, s'ignoren les receptes amb el mateix nom que les " "existents. Marqueu aquesta casella per importar-ho tot." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Afegir el teu comentari: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" "Deixeu-lo buit per a Dropbox i introduïu la contrasenya de l'aplicació per a " "nextcloud." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "Deixeu-lo buit per a nextcloud i introduïu el token API per a Dropbox." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -213,33 +213,33 @@ msgstr "" "Deixeu-lo buit per a Dropbox i introduïu només l'URL base per a Nextcloud " "(/remote.php/webdav/ s'afegeix automàticament)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Emmagatzematge" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Actiu" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Cerca Cadena" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "ID d'Arxiu" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Has de proporcionar com a mínim una recepta o un títol." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Podeu llistar els usuaris predeterminats amb els quals voleu compartir " "receptes a la configuració." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -247,15 +247,15 @@ msgstr "" "Podeu utilitzar el marcador per donar format a aquest camp. Consulteu els documents aquí " -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Nombre màxim d'usuaris assolit per a aquest espai." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Adreça de correu electrònic existent!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -263,15 +263,15 @@ msgstr "" "No cal una adreça de correu electrònic, però si està present, s'enviarà " "l'enllaç d'invitació a l'usuari." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Nom agafat." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Accepteu les condicions i la privadesa" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -280,7 +280,7 @@ msgstr "" "de trigrama (p. ex., els valors baixos signifiquen que s'ignoren més errors " "ortogràfics)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -288,7 +288,7 @@ msgstr "" "Seleccioneu el tipus de mètode de cerca. Feu clic aquí per obtenir una descripció completa de les opcions." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -296,7 +296,7 @@ msgstr "" "Utilitzeu la concordança difusa en unitats, paraules clau i ingredients quan " "editeu i importeu receptes." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -304,7 +304,7 @@ msgstr "" "Camps per cercar ignorant els accents. La selecció d'aquesta opció pot " "millorar o degradar la qualitat de la cerca en funció de l'idioma" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" @@ -312,7 +312,7 @@ msgstr "" "Camps per cercar coincidències parcials. (p. ex., en cercar \"Pastís\" " "tornarà \"pastís\" i \"peça\" i \"sabó\")" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -320,7 +320,7 @@ msgstr "" "Camps per cercar l'inici de les coincidències de paraula. (p. ex., en cercar " "\"sa\" es tornarà \"amanida\" i \"entrepà\")" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -329,7 +329,7 @@ msgstr "" "trobareu \"recepta\".) Nota: aquesta opció entrarà en conflicte amb els " "mètodes de cerca \"web\" i \"cru\"." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -337,35 +337,35 @@ msgstr "" "Camps per a la cerca de text complet. Nota: els mètodes de cerca \"web\", " "\"frase\" i \"en brut\" només funcionen amb camps de text complet." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Mètode de cerca" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Cerques difuses" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Ignora Accents" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Cerca Parcial" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Comença amb" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Cerca Difusa" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Text Sencer" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -373,7 +373,7 @@ msgstr "" "Els usuaris veuran tots els articles que afegiu a la vostra llista de la " "compra. Us han d'afegir per veure els elements de la seva llista." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -381,7 +381,7 @@ msgstr "" "Quan afegiu un pla d'àpats a la llista de la compra (de manera manual o " "automàtica), inclou totes les receptes relacionades." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -389,95 +389,95 @@ msgstr "" "Quan afegiu un pla d'àpats a la llista de la compra (manual o " "automàticament), excloeu els ingredients que teniu a mà." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" "Nombre d'hores per defecte per retardar l'entrada d'una llista de la compra." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Filtreu la llista de compres per incloure només categories de supermercats." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "Dies de les entrades recents de la llista de la compra per mostrar." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "Marca el menjar com a \"A mà\" quan marqueu la llista de la compra." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Delimitador per a les exportacions CSV." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "Prefix per afegir en copiar la llista al porta-retalls." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Compartir Llista de la Compra" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" -msgstr "Autosync" +msgstr "Autosinc" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Afegeix automàticament un pla d'àpats" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Exclou a mà" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Incloure Relacionats" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Hores de retard per defecte" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Filtrar a supermercat" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Dies recents" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "Delimitador CSV" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Prefix de Llista" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Auto a mà" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Restablir Herència Alimentària" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Restableix tots els aliments per heretar els camps configurats." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "Camps dels aliments que s'han d'heretar per defecte." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Mostra el recompte de receptes als filtres de cerca" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." -msgstr "" +msgstr "Empra el plural d'aquestes unitats i menjars dins de l'espai." #: .\cookbook\helper\AllAuthCustomAdapter.py:39 msgid "" @@ -488,7 +488,7 @@ msgstr "" "uns minuts i torneu-ho a provar." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "No heu iniciat la sessió, no podeu veure aquesta pàgina." @@ -501,7 +501,7 @@ msgstr "No heu iniciat la sessió, no podeu veure aquesta pàgina." #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "No teniu els permisos necessaris per veure aquesta pàgina!" @@ -521,11 +521,41 @@ msgstr "Has arribat al nombre màxim de receptes per al vostre espai." msgid "You have more users than allowed in your space." msgstr "Tens més usuaris dels permesos al teu espai." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "S'ha de proporcionar una de queryset o hash_key" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "Use fractions" +msgid "reverse rotation" +msgstr "Utilitza fraccions" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Heu de proporcionar una mida de porcions" @@ -543,7 +573,7 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -551,7 +581,7 @@ msgstr "" "S'esperava un fitxer .zip. Heu escollit el tipus d'importador correcte per a " "les vostres dades?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -559,24 +589,30 @@ msgstr "" "S'ha produït un error inesperat durant la importació. Assegureu-vos que heu " "penjat un fitxer vàlid." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "Les receptes següents s'han ignorat perquè ja existien:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "%s Receptes Importades." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +#, fuzzy +#| msgid "Recipe Home" +msgid "Recipe source:" +msgstr "Receptari" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Notes" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Informació Nutricional" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Font" @@ -647,78 +683,78 @@ msgstr "" "Emmagatzematge màxim de fitxers per espai en MB. 0 per il·limitat, -1 per " "desactivar la càrrega de fitxers." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Cerca" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Plans de Menjar" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Receptes" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " forma part d'un pas de recepta i no es pot suprimir" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Simple" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Frase" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Cru" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Alies Menjar" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Àlies Unitat" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Àlies Paraula clau" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Description" msgid "Description Replace" msgstr "Descripció" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Instructions" msgid "Instruction Replace" msgstr "Instruccions" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Recepta" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 #, fuzzy #| msgid "Foods" msgid "Food" msgstr "Menjars" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Paraula Clau" @@ -734,47 +770,47 @@ msgstr "Límit de càrrega de fitxers Assolit." msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Hola" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Convidat per " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " per unir-se al seu espai de Receptes " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Click per activar el teu compte: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Si l'enllaç no funciona, utilitzeu el codi següent per unir-vos a l'espai: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "Invitació vàlida fins " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "Tandoor Recipes és un gestor de receptes de codi obert. Comprova a GitHub " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Invitació de receptes Tandoor" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Llista de la compra existent a actualitzar" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -782,22 +818,22 @@ msgstr "" "Llista d'ingredients IDs de la recepta per afegir, si no es proporciona, " "s'afegiran tots els ingredients." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" "Proporcionant un list_recipe ID i porcions de 0, se suprimirà aquesta llista " "de la compra." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "Quantitat de menjar per afegir a la llista de la compra" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "ID de la unitat a utilitzar per a la llista de la compra" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" "Quan s'estableix a true, se suprimirà tots els aliments de les llistes de " @@ -1414,7 +1450,7 @@ msgstr "" #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "Cerca Recepta..." +msgstr "Cerca Recepta ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" @@ -1642,11 +1678,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "per" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Comentari" @@ -2177,81 +2213,81 @@ msgstr "" msgid "URL Import" msgstr "Importació d’URL" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "El paràmetre updated_at té un format incorrecte" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "No {self.basename} amb id {pk} existeix" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "No es pot fusionar amb el mateix objecte!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "No {self.basename} amb id {target} existeix" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "No es pot combinar amb l'objecte fill!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} s'ha fusionat amb {target.name}" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "Error en intentar combinar {source.name} amb {target.name}" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} s'ha mogut correctament a l'arrel." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Error a l'intentar moure " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "No es pot moure un objecte cap a si mateix!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "No existeix {self.basename} amb identificador {parent}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} s'ha mogut correctament al pare {parent.name}" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} eliminat de la llista de la compra." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "Afegit {obj.name} a la llista de la compra." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "ID de recepta forma part d'un pas. Per a múltiples repeteix paràmetre." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "La cadena de consulta coincideix (difusa) amb el nom de l'objecte." -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." @@ -2259,7 +2295,7 @@ msgstr "" "Cadena de consulta coincideix (difusa) amb el nom de la recepta. En el futur " "també cerca text complet." -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 #, fuzzy #| msgid "ID of keyword a recipe should have. For multiple repeat parameter." msgid "" @@ -2269,177 +2305,177 @@ msgstr "" "ID de la paraula clau que hauria de tenir una recepta. Per a múltiples " "repeteix paràmetre." -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" "ID d'aliments que ha de tenir una recepta. Per a múltiples repeteix " "paràmetres." -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "ID d'unitat que hauria de tenir una recepta." -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" "ID del llibre hauria d'haver-hi en una recepta. Per al paràmetre de " "repetició múltiple." -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Res a fer." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Connexió Refusada." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "No s'han trobat dades utilitzables." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "Importació no implementada en aquest proveïdor" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" "Aquesta funció encara no està disponible a la versió allotjada de tandoor!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Sincronització correcte" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Error de sincronització amb emmagatzematge" @@ -2505,7 +2541,7 @@ msgstr "Canvis desats!" msgid "Error saving changes!" msgstr "Error al desar canvis!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2555,7 +2591,12 @@ msgstr "Nova Recepta importada!" msgid "There was an error importing this recipe!" msgstr "S'ha produït un error en importar la recepta!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Funció no està disponible a la versió de demostració!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2563,24 +2604,19 @@ msgstr "" "Espai de Receptes creat correctament. Comenceu afegint algunes receptes o " "convida altres persones a unir-se." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "No teniu els permisos necessaris per dur a terme aquesta acció!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Comentari Desat!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Funció no està disponible a la versió de demostració!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "Heu de seleccionar almenys un camp per cercar!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2588,11 +2624,11 @@ msgstr "" "Per utilitzar aquest mètode de cerca, heu de seleccionar almenys un camp de " "cerca de text complet!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "Cerca difusa no és compatible amb aquest mètode de cerca!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2602,27 +2638,27 @@ msgstr "" "Si heu oblidat les vostres credencials de superusuari, consulteu la " "documentació de django sobre com restablir les contrasenyes." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Les contrasenyes no coincideixen!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "L'usuari s'ha creat, si us plau inicieu la sessió!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "S'ha proporcionat un enllaç d'invitació mal format." -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Unit correctament a l'espai." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "L'enllaç d'invitació no és vàlid o ja s'ha utilitzat." -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2630,7 +2666,7 @@ msgstr "" "Notificació d'enllaços compartits no activada en aquesta instància. Aviseu " "l'administrador per informar dels problemes." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/cs/LC_MESSAGES/django.mo b/cookbook/locale/cs/LC_MESSAGES/django.mo index 617c31e76..3853f5ecb 100644 Binary files a/cookbook/locale/cs/LC_MESSAGES/django.mo and b/cookbook/locale/cs/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/cs/LC_MESSAGES/django.po b/cookbook/locale/cs/LC_MESSAGES/django.po index 18f4d74f6..513d8c041 100644 --- a/cookbook/locale/cs/LC_MESSAGES/django.po +++ b/cookbook/locale/cs/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-09 18:01+0100\n" -"PO-Revision-Date: 2023-03-25 11:32+0000\n" -"Last-Translator: Matěj Kubla \n" +"PO-Revision-Date: 2023-07-31 14:19+0000\n" +"Last-Translator: Mára Štěpánek \n" "Language-Team: Czech \n" "Language: cs\n" @@ -36,7 +36,7 @@ msgid "" "try them out!" msgstr "" "Barva horního navigačního menu. Některé barvy neladí se všemi tématy a je " -"třeba je vyzkoušet." +"třeba je vyzkoušet!" #: .\cookbook\forms.py:45 msgid "Default Unit to be used when inserting a new ingredient into a recipe." @@ -50,7 +50,7 @@ msgid "" "to fractions automatically)" msgstr "" "Povolit podporu zlomků u množství ingrediencí (desetinná čísla budou " -"automaticky převedena na zlomky)." +"automaticky převedena na zlomky)" #: .\cookbook\forms.py:47 msgid "" diff --git a/cookbook/locale/da/LC_MESSAGES/django.mo b/cookbook/locale/da/LC_MESSAGES/django.mo index f0c157bec..8da304df2 100644 Binary files a/cookbook/locale/da/LC_MESSAGES/django.mo and b/cookbook/locale/da/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/da/LC_MESSAGES/django.po b/cookbook/locale/da/LC_MESSAGES/django.po index 7ab8150be..8d3738735 100644 --- a/cookbook/locale/da/LC_MESSAGES/django.po +++ b/cookbook/locale/da/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-04-29 18:42+0200\n" -"PO-Revision-Date: 2023-03-06 10:55+0000\n" -"Last-Translator: Anders Obro \n" +"PO-Revision-Date: 2023-04-12 11:55+0000\n" +"Last-Translator: noxonad \n" "Language-Team: Danish \n" "Language: da\n" @@ -1806,7 +1806,7 @@ msgid "" msgstr "" " \n" " Heltekstsøgning forsøger at normalisere de givne ord så de " -"matcher stammevarianter. F.eks: 'skeen', 'skeer' og 'sket' vil alt " +"matcher stammevarianter. F.eks: 'skeen', 'skeer' og 'sket' vil alt " "normaliseres til 'ske'.\n" " Der er flere metoder tilgængelige, beskrevet herunder, som vil " "bestemme hvordan søgningen skal opfører sig når flere søgeord er angivet.\n" diff --git a/cookbook/locale/de/LC_MESSAGES/django.mo b/cookbook/locale/de/LC_MESSAGES/django.mo index 27e26294b..78118e92a 100644 Binary files a/cookbook/locale/de/LC_MESSAGES/django.mo and b/cookbook/locale/de/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/de/LC_MESSAGES/django.po b/cookbook/locale/de/LC_MESSAGES/django.po index eb97192d2..9043f3b7d 100644 --- a/cookbook/locale/de/LC_MESSAGES/django.po +++ b/cookbook/locale/de/LC_MESSAGES/django.po @@ -14,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2023-02-09 13:55+0000\n" -"Last-Translator: Marion Kämpfer \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-08-13 08:19+0000\n" +"Last-Translator: Fabian Flodman \n" "Language-Team: German \n" "Language: de\n" @@ -66,7 +66,7 @@ msgstr "Nachkommastellen für Zutaten" msgid "Shopping list auto sync period" msgstr "Synchronisierungshäufigkeit der Einkaufsliste" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Kommentare" @@ -119,7 +119,7 @@ msgstr "" "Wenn du in der Lage sein willst, Kommentare unter Rezepten zu erstellen und " "zu sehen." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -135,7 +135,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Navigationsleiste wird oben angeheftet." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "Fügt die Zutaten des Speiseplans automatisch zur Einkaufsliste hinzu." @@ -155,11 +155,11 @@ msgstr "" "Beide Felder sind optional. Wenn keins von beiden gegeben ist, wird der " "Nutzername angezeigt" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Name" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Stichwörter" @@ -171,7 +171,7 @@ msgstr "Zubereitungszeit in Minuten" msgid "Waiting time (cooking/baking) in minutes" msgstr "Wartezeit (kochen/backen) in Minuten" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Pfad" @@ -179,11 +179,11 @@ msgstr "Pfad" msgid "Storage UID" msgstr "Speicher-UID" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Standard" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -191,19 +191,19 @@ msgstr "" "Um Duplikate zu vermeiden werden Rezepte mit dem gleichen Namen ignoriert. " "Aktivieren Sie dieses Kontrollkästchen, um alles zu importieren." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Schreibe einen Kommentar: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "Für Dropbox leer lassen, bei Nextcloud App-Passwort eingeben." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "Für Nextcloud leer lassen, für Dropbox API-Token eingeben." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -211,33 +211,33 @@ msgstr "" "Für Dropbox leer lassen, für Nextcloud Server-URL angeben (/remote.php/" "webdav/ wird automatisch hinzugefügt)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Speicher" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Aktiv" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Suchwort" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "Datei-ID" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Mindestens ein Rezept oder ein Titel müssen angegeben werden." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Sie können in den Einstellungen Standardbenutzer auflisten, für die Sie " "Rezepte freigeben möchten." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -245,15 +245,15 @@ msgstr "" "Markdown kann genutzt werden, um dieses Feld zu formatieren. Siehe hier für weitere Information" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Maximale Nutzer-Anzahl wurde für diesen Space erreicht." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Email-Adresse ist bereits vergeben!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -261,15 +261,15 @@ msgstr "" "Eine Email-Adresse wird nicht benötigt, aber falls vorhanden, wird der " "Einladungslink zum Benutzer geschickt." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Name wird bereits verwendet." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "AGB und Datenschutzerklärung akzeptieren" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -277,7 +277,7 @@ msgstr "" "Legt fest wie unscharf eine Suche ist, falls Trigramme verwendet werden (i." "A. führen niedrigere Werte zum ignorieren von mehr Tippfehlern)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -285,7 +285,7 @@ msgstr "" "Suchmethode auswählen. Klicke hier für eine " "vollständige Erklärung der Optionen." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -293,23 +293,23 @@ msgstr "" "Benutze die unscharfe Suche für Einheiten, Schlüsselwörter und Zutaten beim " "ändern und importieren von Rezepten." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" -"Felder bei welchen Akzente ignoriert werden. Das aktivieren dieser Option " +"Felder bei welchen Akzente ignoriert werden. Das aktivieren dieser Option " "kann die Suchqualität je nach Sprache verbessern oder verschlechtern" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" "Felder welche auf partielle Treffer durchsucht werden. (z.B. eine Suche " -"nach \"Spa\" wird \"Spaghetti\", \"Spargel\" und \"Grünspargel\" liefern.)" +"nach 'Spa' wird 'Spaghetti', 'Spargel' und 'Grünspargel' liefern.)" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -317,7 +317,7 @@ msgstr "" "Felder welche auf übereinstimmenden Wortbeginn durchsucht werden. (z.B. eine " "Suche nach \"Spa\" wird \"Spaghetti\" und \"Spargel\" liefern.)" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -326,7 +326,7 @@ msgstr "" "\"Kuhcen\" wird \"Kuchen\" liefern.) Tipp: Diese Option konfligiert mit den " "\"web\" und \"raw\" Suchtypen." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -334,35 +334,35 @@ msgstr "" "Felder welche im Volltext durchsucht werden sollen. Tipp: Die Suchtypen \"web" "\", \"raw\" und \"phrase\" funktionieren nur mit Volltext-Feldern." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Suchmethode" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Unscharfe Suche" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Akzente ignorieren" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Teilweise Übereinstimmung" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Beginnt mit" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Unpräzise Suche" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Volltext" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -371,7 +371,7 @@ msgstr "" "Benutzer müssen Sie hinzufügen, damit Sie Artikel auf der Liste der Benutzer " "sehen können." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -379,7 +379,7 @@ msgstr "" "Wenn Sie einen Essensplan zur Einkaufsliste hinzufügen (manuell oder " "automatisch), fügen Sie alle zugehörigen Rezepte hinzu." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -387,98 +387,98 @@ msgstr "" "Wenn Sie einen Essensplan zur Einkaufsliste hinzufügen (manuell oder " "automatisch), schließen Sie Zutaten aus, die Sie gerade zur Hand haben." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" "Voreingestellte Anzahl von Stunden für die Verzögerung eines " "Einkaufslisteneintrags." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Nur für den Supermarkt konfigurierte Kategorien in Einkaufsliste anzeigen." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "" "Tage der letzten Einträge in der Einkaufsliste, die angezeigt werden sollen." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Lebensmittel als vorrätig markieren, wenn es in der Einkaufliste abgehakt " "wurde." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Separator für CSV-Export." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "Zusatz wird der in die Zwischenablage kopierten Liste vorangestellt." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Einkaufsliste teilen" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Automatischer Abgleich" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "automatisch dem Menüplan hinzufügen" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Ausgenommen Vorrätiges" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "dazugehörend" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Standardmäßige Verzögerung in Stunden" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Supermarkt filtern" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Vergangene Tage" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "CSV Trennzeichen" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Listenpräfix" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Automatisch als vorrätig markieren" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Lebensmittelvererbung zurücksetzen" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "" "Alle Lebensmittel zurücksetzen, um die konfigurierten Felder zu übernehmen." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "Zutaten, die standardmäßig übernommen werden sollen." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Rezeptanzahl im Suchfiltern anzeigen" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "Pluralform für Einheiten und Essen in diesem Space verwenden." @@ -491,7 +491,7 @@ msgstr "" "warte ein paar Minuten und versuche es erneut." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Du bist nicht angemeldet, daher kannst du diese Seite nicht sehen!" @@ -504,7 +504,7 @@ msgstr "Du bist nicht angemeldet, daher kannst du diese Seite nicht sehen!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Du hast nicht die notwendigen Rechte um diese Seite zu sehen!" @@ -524,40 +524,39 @@ msgstr "Du hast die maximale Anzahl an Rezepten für Deinen Space erreicht." msgid "You have more users than allowed in your space." msgstr "Du hast mehr Benutzer in Deinem Space als erlaubt." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "Es muss die Abfrage oder der Hash_Key angeben werden" -#: .\cookbook\helper\recipe_url_import.py:265 +#: .\cookbook\helper\recipe_url_import.py:266 msgid "reverse rotation" msgstr "Linkslauf" -#: .\cookbook\helper\recipe_url_import.py:266 +#: .\cookbook\helper\recipe_url_import.py:267 msgid "careful rotation" msgstr "Kochlöffel" -#: .\cookbook\helper\recipe_url_import.py:267 +#: .\cookbook\helper\recipe_url_import.py:268 msgid "knead" msgstr "Kneten" -#: .\cookbook\helper\recipe_url_import.py:268 +#: .\cookbook\helper\recipe_url_import.py:269 msgid "thicken" msgstr "Andicken" -#: .\cookbook\helper\recipe_url_import.py:269 +#: .\cookbook\helper\recipe_url_import.py:270 msgid "warm up" msgstr "Erwärmen" -#: .\cookbook\helper\recipe_url_import.py:270 +#: .\cookbook\helper\recipe_url_import.py:271 msgid "ferment" msgstr "Fermentieren" -#: .\cookbook\helper\recipe_url_import.py:271 +#: .\cookbook\helper\recipe_url_import.py:272 msgid "sous-vide" msgstr "Sous-vide" - -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Sie müssen eine Portionsgröße angeben" @@ -575,7 +574,7 @@ msgstr "Favorit" msgid "I made this" msgstr "Von mir gekocht" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -583,7 +582,7 @@ msgstr "" "Importer erwartet eine .zip Datei. Hast du den richtigen Importer-Typ für " "deine Daten ausgewählt?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -591,24 +590,28 @@ msgstr "" "Ein unerwarteter Fehler trat beim Importieren auf. Bitte stelle sicher, dass " "die hochgeladene Datei gültig ist." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "Die folgenden Rezepte wurden ignoriert da sie bereits existieren:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "%s Rezepte importiert." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" +msgstr "Rezept-Quelle:" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Notizen" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Nährwert Informationen" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Quelle" @@ -679,76 +682,72 @@ msgstr "" "Maximale Datei-Speichergröße in MB. 0 für unbegrenzt, -1 um den Datei-Upload " "zu deaktivieren." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Suchen" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Essensplan" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Bücher" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " ist Teil eines Rezepts und kann nicht gelöscht werden" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Einfach" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Satz" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Rohdaten" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Lebensmittel Alias" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Einheiten Alias" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Stichwort Alias" -#: .\cookbook\models.py:1231 -#, fuzzy -#| msgid "Description" +#: .\cookbook\models.py:1232 msgid "Description Replace" -msgstr "Beschreibung" +msgstr "Beschreibung ersetzen" -#: .\cookbook\models.py:1231 -#, fuzzy -#| msgid "Instructions" +#: .\cookbook\models.py:1232 msgid "Instruction Replace" -msgstr "Anleitung" +msgstr "Anleitung ersetzen" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Rezept" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "Lebensmittel" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Schlüsselwort" @@ -764,49 +763,49 @@ msgstr "Du hast Dein Datei-Uploadlimit erreicht." msgid "Cannot modify Space owner permission." msgstr "Die Eigentumsberechtigung am Space kann nicht geändert werden." -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Hallo" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Du wurdest eingeladen von " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " um deren Tandoor Recipes Instanz beizutreten " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Klicke auf den folgenden Link, um deinen Account zu aktivieren: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Falls der Link nicht funktioniert, benutze den folgenden Code um dem Space " "manuell beizutreten: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "Die Einladung ist gültig bis " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "Tandoor Recipes ist ein Open-Source Rezept-Manager. Mehr Informationen sind " "auf GitHub zu finden " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Tandoor Recipes Einladung" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Bestehende Einkaufliste, die aktualisiert werden soll" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -814,23 +813,23 @@ msgstr "" "Liste der Zutaten-IDs aus dem Rezept, wenn keine Angabe erfolgt, werden alle " "Zutaten hinzugefügt." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" "Wenn Sie eine list_recipe ID und Portion mit dem Wert 0 angeben, wird diese " "Einkaufsliste gelöscht." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" "Menge des Lebensmittels, welches der Einkaufsliste hinzugefügt werden soll" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "ID der Einheit, die für die Einkaufsliste verwendet werden soll" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" "Wenn diese Option aktiviert ist, werden alle Lebensmittel aus den aktiven " @@ -963,7 +962,7 @@ msgid "" " issue a new e-mail confirmation " "request." msgstr "" -"Dieser Email-Bestätigungslink ist abgelaufen oder ungültig. Bitte \n" +"Dieser Email-Bestätigungslink ist abgelaufen oder ungültig. Bitte\n" " beantrage einen neuen Email-" "Bestätigungslink." @@ -1079,7 +1078,7 @@ msgstr "" #: .\cookbook\templates\account\password_reset_from_key.html:33 msgid "change password" -msgstr "Passwort ändern" +msgstr "passwort ändern" #: .\cookbook\templates\account\password_reset_from_key.html:36 #: .\cookbook\templates\account\password_reset_from_key_done.html:19 @@ -1252,7 +1251,7 @@ msgstr "Ausloggen" #: .\cookbook\templates\base.html:360 msgid "You are using the free version of Tandor" -msgstr "Du benützt die Gratis-Version von Tandoor" +msgstr "Du benutzt die Gratis-Version von Tandoor" #: .\cookbook\templates\base.html:361 msgid "Upgrade Now" @@ -1437,18 +1436,18 @@ msgid "" " " msgstr "" "\n" -" Password und Token werden im Klartext in der Datenbank " +" Passwort und Token werden im Klartext in der Datenbank " "gespeichert.\n" " Dies ist notwendig da Passwort oder Token benötigt werden, um API-" -"Anfragen zu stellen, bringt jedoch auch ein Sicherheitsrisiko mit sich.
    \n" +"Anfragen zu stellen, bringt jedoch auch ein Sicherheitsrisiko mit sich.
    " +"\n" " Um das Risiko zu minimieren sollten, wenn möglich, Tokens oder " "Accounts mit limitiertem Zugriff verwendet werden.\n" " " #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "Rezept suchen..." +msgstr "Rezept suchen ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" @@ -1674,11 +1673,11 @@ msgstr "Zurück" msgid "Profile" msgstr "Profil" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "von" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Kommentar" @@ -1733,8 +1732,8 @@ msgid "" msgstr "" " \n" " Die Volltextsuche versucht Wörter in übliche Varianten zu " -"normalisieren. z.B.: \"Kürbis\", \"Kürbissuppe\", \"Kürbiskuchen\" werden " -"alle zu \"Kürbis\" normalisiert..\n" +"normalisieren. z.B.: 'Kürbis', 'Kürbissuppe', 'Kürbiskuchen' werden alle zu " +"'Kürbis' normalisiert..\n" " Es sind verschiedene Methoden verfügbar, welche weiter unten " "genau beschrieben werden. Diese beeinflussen das Suchergebnis bei einer " "Suche mit mehreren Wörtern.\n" @@ -1937,7 +1936,7 @@ msgstr "" " Die Indizes für alle Felder können auf der Admin Seite neu " "erstellt werden.'\n" " Ansonsten können die Indizes auch mit dem management command " -"\"python manage.py rebuildindex\" neu erstellt werden.\n" +"'python manage.py rebuildindex' neu erstellt werden.\n" " " #: .\cookbook\templates\settings.html:25 @@ -2307,85 +2306,85 @@ msgstr "" msgid "URL Import" msgstr "URL-Import" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "Der Parameter updated_at ist falsch formatiert" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "Kein {self.basename} mit der ID {pk} existiert" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "Zusammenführen mit selben Objekt nicht möglich!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "Kein {self.basename} mit der ID {target} existiert" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "Zusammenführen mit untergeordnetem Objekt nicht möglich!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} wurde erfolgreich mit {target.name} zusammengeführt" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" "Beim zusammenführen von {source.name} mit {target.name} ist ein Fehler " "aufgetreten" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} wurde erfolgreich zur Wurzel verschoben." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Fehler aufgetreten beim verschieben von " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "Ein Element kann nicht in sich selbst verschoben werden!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "Kein {self.basename} mit ID {parent} existiert" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" "{child.name} wurde erfolgreich zum Überelement {parent.name} verschoben" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} wurde von der Einkaufsliste entfernt." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} wurde der Einkaufsliste hinzugefügt." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" "ID des Rezeptes zu dem ein Schritt gehört. Kann mehrfach angegeben werden." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "Abfragezeichenfolge, die mit dem Objektnamen übereinstimmt (ungenau)." -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." @@ -2393,7 +2392,7 @@ msgstr "" "Suchbegriff wird mit dem Rezeptnamen abgeglichen. In Zukunft auch " "Volltextsuche." -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" @@ -2401,69 +2400,69 @@ msgstr "" "ID des Stichwortes, das ein Rezept haben muss. Kann mehrfach angegeben " "werden. Äquivalent zu keywords_or" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" "Stichwort IDs. Kann mehrfach angegeben werden. Listet Rezepte zu jedem der " "angegebenen Stichwörter" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" "Stichwort IDs. Kann mehrfach angegeben werden. Listet Rezepte mit allen " "angegebenen Stichwörtern." -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" "Stichwort ID. Kann mehrfach angegeben werden. Schließt Rezepte einem der " "angegebenen Stichwörtern aus." -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" "Stichwort IDs. Kann mehrfach angegeben werden. Schließt Rezepte mit allen " "angegebenen Stichwörtern aus." -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" "ID einer Zutat, zu der Rezepte gelistet werden sollen. Kann mehrfach " "angegeben werden." -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" "Zutat ID. Kann mehrfach angegeben werden. Listet Rezepte mindestens einer " "der Zutaten" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" "Zutat ID. Kann mehrfach angegeben werden. Listet Rezepte mit allen " "angegebenen Zutaten." -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" "Zutat ID. Kann mehrfach angegeben werden. Schließt Rezepte aus, die eine der " "angegebenen Zutaten enthalten." -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" "Zutat ID. Kann mehrfach angegeben werden. Schließt Rezepte aus, die alle " "angegebenen Zutaten enthalten." -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "ID der Einheit, die ein Rezept haben sollte." -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." @@ -2471,50 +2470,50 @@ msgstr "" "Mindestbewertung eines Rezeptes (0-5). Negative Werte filtern nach " "Maximalbewertung." -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "Buch ID, in dem das Rezept ist. Kann mehrfach angegeben werden." -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" "Buch ID. Kann mehrfach angegeben werden. Listet alle Rezepte aus den " "angegebenen Büchern" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" "Buch ID. Kann mehrfach angegeben werden. Listet die Rezepte, die in allen " "Büchern enthalten sind." -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" "Buch IDs. Kann mehrfach angegeben werden. Schließt Rezepte aus den " "angegebenen Büchern aus." -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" "Buch IDs. Kann mehrfach angegeben werden. Schließt Rezepte aus, die in allen " "angegebenen Büchern enthalten sind." -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "Nur interne Rezepte sollen gelistet werden. [ja/nein]" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" "Die Suchergebnisse sollen in zufälliger Reihenfolge gelistet werden. [ja/" "nein]" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" "Die neuesten Suchergebnisse sollen zuerst angezeigt werden. [ja/nein]" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" @@ -2522,7 +2521,7 @@ msgstr "" "Rezepte listen, die mindestens x-mal gekocht wurden. Eine negative Zahl " "listet Rezepte, die weniger als x-mal gekocht wurden" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." @@ -2531,7 +2530,7 @@ msgstr "" "wurden. Mit vorangestelltem - , werden Rezepte am oder vor dem Datum " "gelistet." -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." @@ -2539,7 +2538,7 @@ msgstr "" "Rezepte listen, die am angegebenen Datum oder später erstellt wurden. Wenn - " "vorangestellt wird, wird am oder vor dem Datum gelistet." -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." @@ -2547,7 +2546,7 @@ msgstr "" "Rezepte listen, die am angegebenen Datum oder später aktualisiert wurden. " "Wenn - vorangestellt wird, wird am oder vor dem Datum gelistet." -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." @@ -2555,13 +2554,13 @@ msgstr "" "Rezepte listen, die am angegebenen Datum oder später zuletzt angesehen " "wurden. Wenn - vorangestellt wird, wird am oder vor dem Datum gelistet." -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" "Rezepte listen, die mit vorhandenen Zutaten gekocht werden können. [ja/" "nein]" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." @@ -2569,7 +2568,7 @@ msgstr "" "Zeigt denjenigen Eintrag auf der Einkaufliste mit der angegebenen ID. Kann " "mehrfach angegeben werden." -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." @@ -2578,45 +2577,45 @@ msgstr "" "kürzlich]
    - kürzlich enthält nicht abgehakte Einträge und " "kürzlich abgeschlossene Einträge." -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" "Listet die Einträge der Einkaufsliste sortiert nach Supermarktkategorie." -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Nichts zu tun." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "Ungültige URL" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Verbindung fehlgeschlagen." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "Ungültiges URL Schema." -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." -msgstr "Es konnten keine nutzbaren Daten gefunden werden." +msgstr "Es konnten keine passenden Daten gefunden werden." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "Importieren ist für diesen Anbieter noch nicht implementiert" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "Diese Funktion ist in dieser Version von Tandoor noch nicht verfügbar!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Synchronisation erfolgreich!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Fehler beim Synchronisieren" @@ -2680,7 +2679,7 @@ msgstr "Änderungen gespeichert!" msgid "Error saving changes!" msgstr "Fehler beim Speichern der Daten!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2728,7 +2727,12 @@ msgstr "Neues Rezept importiert!" msgid "There was an error importing this recipe!" msgstr "Beim Importieren des Rezeptes ist ein Fehler aufgetreten!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Diese Funktion ist in der Demo-Version nicht verfügbar!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2736,25 +2740,20 @@ msgstr "" "Du hast erfolgreich deinen eigenen Rezept-Space erstellt. Beginne, indem Du " "ein paar Rezepte hinzufügst oder weitere Leute einlädst." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "" "Du hast nicht die notwendige Berechtigung, um diese Aktion durchzuführen!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Kommentar gespeichert!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Diese Funktion ist in der Demo-Version nicht verfügbar!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "Es muss mindestens ein Feld ausgewählt sein!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2762,11 +2761,11 @@ msgstr "" "Um diese Suchmethode zu verwenden muss mindestens ein Feld für die " "Volltextsuche ausgewählt sein!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "Die \"Ungenaue\" Suche ist mit diesem Suchtyp nicht kompatibel!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2775,27 +2774,27 @@ msgstr "" "Die Setup-Seite kann nur für den ersten Nutzer verwendet werden. Zum " "Zurücksetzen von Passwörtern bitte der Django-Dokumentation folgen." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Passwörter stimmen nicht überein!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "Benutzer wurde erstellt, bitte einloggen!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "Fehlerhafter Einladungslink angegeben!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Space erfolgreich beigetreten." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "Einladungslink ungültig oder bereits genutzt!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2803,7 +2802,7 @@ msgstr "" "Das melden von Links ist in dieser Instanz nicht aktiviert. Bitte " "kontaktieren sie den Seitenadministrator um Probleme zu melden." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." @@ -3075,7 +3074,6 @@ msgstr "" #~ msgstr "Erledigt" #, fuzzy -#~| msgid "You are offline, shopping list might not syncronize." #~ msgid "You are offline, shopping list might not synchronize." #~ msgstr "Du bist offline, die Einkaufsliste wird ggf. nicht synchronisiert." @@ -3500,7 +3498,6 @@ msgstr "" #~ msgstr "Datei auswählen" #, fuzzy -#~| msgid "Delete Recipe" #~ msgid "Select Recipe" #~ msgstr "Rezept löschen" diff --git a/cookbook/locale/el/LC_MESSAGES/django.mo b/cookbook/locale/el/LC_MESSAGES/django.mo index 9771bde9e..b4b07db67 100644 Binary files a/cookbook/locale/el/LC_MESSAGES/django.mo and b/cookbook/locale/el/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/el/LC_MESSAGES/django.po b/cookbook/locale/el/LC_MESSAGES/django.po index 1208f55ec..03e0ec40a 100644 --- a/cookbook/locale/el/LC_MESSAGES/django.po +++ b/cookbook/locale/el/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-07-12 19:20+0200\n" -"PO-Revision-Date: 2022-10-17 11:33+0000\n" -"Last-Translator: Sokratis Potamias \n" +"PO-Revision-Date: 2023-08-21 09:19+0000\n" +"Last-Translator: Theodoros Grammenos \n" "Language-Team: Greek \n" "Language: el\n" @@ -17,24 +17,24 @@ 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.10.1\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\forms\ingredients.html:34 #: .\cookbook\templates\stats.html:28 msgid "Ingredients" -msgstr "Συστατικά" +msgstr "Υλικά" #: .\cookbook\forms.py:53 msgid "Default unit" -msgstr "" +msgstr "Προεπιλεγμένη μονάδα μέτρησης" #: .\cookbook\forms.py:54 msgid "Use fractions" -msgstr "" +msgstr "Χρήση κλασμάτων" #: .\cookbook\forms.py:55 msgid "Use KJ" -msgstr "" +msgstr "Χρήση KiloJoule(KJ)" #: .\cookbook\forms.py:56 msgid "Theme" @@ -42,35 +42,35 @@ msgstr "Θέμα" #: .\cookbook\forms.py:57 msgid "Navbar color" -msgstr "" +msgstr "Χρώμα μπάρας πλοήγησης" #: .\cookbook\forms.py:58 msgid "Sticky navbar" -msgstr "" +msgstr "Σταθερή μπάρα πλοήγησης" #: .\cookbook\forms.py:59 msgid "Default page" -msgstr "" +msgstr "Προεπιλεγμένη σελίδα" #: .\cookbook\forms.py:60 msgid "Show recent recipes" -msgstr "" +msgstr "Προβολή πρόσφατων συνταγών" #: .\cookbook\forms.py:61 msgid "Search style" -msgstr "" +msgstr "Τρόπος αναζήτησης" #: .\cookbook\forms.py:62 msgid "Plan sharing" -msgstr "" +msgstr "Κοινοποίηση προγράμματος" #: .\cookbook\forms.py:63 msgid "Ingredient decimal places" -msgstr "" +msgstr "Δεκαδικά ψηφία υλικών" #: .\cookbook\forms.py:64 msgid "Shopping list auto sync period" -msgstr "" +msgstr "Χρονική περίοδος αυτόματου συγχρονισμού λίστας αγορών" #: .\cookbook\forms.py:65 .\cookbook\templates\recipe_view.html:21 #: .\cookbook\templates\stats.html:47 @@ -79,47 +79,57 @@ msgstr "Σχόλια" #: .\cookbook\forms.py:66 msgid "Left-handed mode" -msgstr "" +msgstr "Έκδοση για αριστερόχειρες" #: .\cookbook\forms.py:70 msgid "" "Color of the top navigation bar. Not all colors work with all themes, just " "try them out!" msgstr "" +"Χρώμα της πάνω μπάρας πλοήγησης. Δεν δουλεύουν όλα τα χρώματα με όλα τα " +"θέματα, απλά δοκιμάστε τα!" #: .\cookbook\forms.py:72 msgid "Default Unit to be used when inserting a new ingredient into a recipe." msgstr "" +"Προεπιλεγμένη μονάδα μέτρησης που θα χρησιμοποιείται όταν προστίθεται ένα " +"υλικό σε μια συνταγή." #: .\cookbook\forms.py:74 msgid "" "Enables support for fractions in ingredient amounts (e.g. convert decimals " "to fractions automatically)" msgstr "" +"Ενεργοποιεί τη υποστήριξη για κλάσματα στις ποσότητες των υλικών (π.χ. " +"μετατρέπει τα δεκαδικά σε κλάσματα αυτόματα)" #: .\cookbook\forms.py:76 msgid "Display nutritional energy amounts in joules instead of calories" -msgstr "" +msgstr "Εμφάνιση της διατροφικής ενεργειακής αξίας σε joules αντί για θερμίδες" #: .\cookbook\forms.py:77 msgid "Users with whom newly created meal plans should be shared by default." msgstr "" +"Χρήστες με του οποίους η κοινοποίηση του προγραμματισμού των γευμάτων θα " +"γίνεται από προεπιλογή." #: .\cookbook\forms.py:78 msgid "Users with whom to share shopping lists." -msgstr "" +msgstr "Χρήστες με του οποίους θα γίνει κοινοποίηση των λιστών αγορών." #: .\cookbook\forms.py:80 msgid "Show recently viewed recipes on search page." -msgstr "" +msgstr "Προβολή των προσφάτως προβεβλημένων συνταγών στη σελίδα αναζήτησης." #: .\cookbook\forms.py:81 msgid "Number of decimals to round ingredients." -msgstr "" +msgstr "Αριθμός των δεκαδικών στα οποία θα γίνεται στρογγυλοποίηση." #: .\cookbook\forms.py:82 msgid "If you want to be able to create and see comments underneath recipes." msgstr "" +"Εάν θέλετε να μπορείτε να δημιουργείτε και να βλέπετε σχόλια κάτω από τις " +"συνταγές." #: .\cookbook\forms.py:84 .\cookbook\forms.py:496 msgid "" @@ -128,169 +138,205 @@ msgid "" "Useful when shopping with multiple people but might use a little bit of " "mobile data. If lower than instance limit it is reset when saving." msgstr "" +"Η ρύθμιση στο 0 θα απενεργοποιήσει τον αυτόματο συγχρονισμό. Όταν προβάλλετε " +"μια λίστα αγορών, η λίστα ενημερώνεται κάθε καθορισμένα δευτερόλεπτα για να " +"συγχρονίσει τις αλλαγές που μπορεί να έχει κάνει κάποιος άλλος. Χρήσιμο όταν " +"ψωνίζετε με πολλούς ανθρώπους, αλλά μπορεί να χρησιμοποιήσει λίγα δεδομένα " +"κινητής τηλεφωνίας. Εάν είναι μικρότερο από το όριο του στιγμιότυπου, " +"επαναφέρεται όταν γίνεται αποθήκευση." #: .\cookbook\forms.py:87 msgid "Makes the navbar stick to the top of the page." -msgstr "" +msgstr "Καθιστά τη γραμμή πλοήγησης κολλημένη στην κορυφή της σελίδας." #: .\cookbook\forms.py:88 .\cookbook\forms.py:499 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" +"Αυτόματη προσθήκη των υλικών του γεύματος που έχει προγραμματιστεί στη λίστα " +"αγορών." #: .\cookbook\forms.py:89 msgid "Exclude ingredients that are on hand." -msgstr "" +msgstr "Αποκλεισμός υλικών που είναι διαθέσιμα." #: .\cookbook\forms.py:90 msgid "Will optimize the UI for use with your left hand." -msgstr "" +msgstr "Θα βελτιστοποιήσει το περιβάλλον χρήστη για χρήση με το αριστερό χέρι." #: .\cookbook\forms.py:107 msgid "" "Both fields are optional. If none are given the username will be displayed " "instead" msgstr "" +"Και τα δύο πεδία είναι προαιρετικά. Αν κανένα δεν συμπληρωθεί, α εμφανιστεί " +"αντί αυτών το όνομα χρήστη" #: .\cookbook\forms.py:128 .\cookbook\forms.py:301 msgid "Name" -msgstr "" +msgstr "Όνομα" #: .\cookbook\forms.py:129 .\cookbook\forms.py:302 #: .\cookbook\templates\stats.html:24 .\cookbook\views\lists.py:88 msgid "Keywords" -msgstr "" +msgstr "Λέξεις κλειδιά" #: .\cookbook\forms.py:130 msgid "Preparation time in minutes" -msgstr "" +msgstr "Χρόνος προετοιμασίας σε λεπτά" #: .\cookbook\forms.py:131 msgid "Waiting time (cooking/baking) in minutes" -msgstr "" +msgstr "Χρόνος αναμονής (μαγείρεμα/ ψήσιμο) σε λεπτά" #: .\cookbook\forms.py:132 .\cookbook\forms.py:270 .\cookbook\forms.py:303 msgid "Path" -msgstr "" +msgstr "Διαδρομή" #: .\cookbook\forms.py:133 msgid "Storage UID" -msgstr "" +msgstr "Αναγνωριστικό αποθήκευσης (Storage UID)" #: .\cookbook\forms.py:165 msgid "Default" -msgstr "" +msgstr "Προεπιλογή" #: .\cookbook\forms.py:177 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." msgstr "" +"Για την αποτροπή της εισαγωγής διπλών συνταγών, συνταγές με το ίδιο όνομα με " +"υπάρχουσες, αγνοούνται. Επιλέξτε αυτό το πλαίσιο για να εισαγάγετε τα πάντα." #: .\cookbook\forms.py:200 msgid "Add your comment: " -msgstr "" +msgstr "Προσθήκη σχολίου: " #: .\cookbook\forms.py:215 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" +"Για dropbox παρακαλώ αφήστε το κενό και πληκτρολογήστε το password για " +"nextcloud." #: .\cookbook\forms.py:222 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" +"Για nextcloud αφήστε το κενό και για dropbox πληκτρολογήστε το api token." #: .\cookbook\forms.py:231 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" msgstr "" +"Αφήστε το κενό για το Dropbox και εισάγετε μόνο τη βασική διεύθυνση URL για " +"το Nextcloud (το /remote.php/webdav/ προστίθεται αυτόματα)" #: .\cookbook\forms.py:269 .\cookbook\views\edit.py:157 msgid "Storage" -msgstr "" +msgstr "Χώρος αποθήκευσης" #: .\cookbook\forms.py:271 msgid "Active" -msgstr "" +msgstr "Ενεργό" #: .\cookbook\forms.py:277 msgid "Search String" -msgstr "" +msgstr "Κείμενο αναζήτησης" #: .\cookbook\forms.py:304 msgid "File ID" -msgstr "" +msgstr "ID αρχείου" #: .\cookbook\forms.py:326 msgid "You must provide at least a recipe or a title." -msgstr "" +msgstr "Πρέπει να παρέχετε τουλάχιστον μια συνταγή ή έναν τίτλο." #: .\cookbook\forms.py:339 msgid "You can list default users to share recipes with in the settings." msgstr "" +"Μπορείτε να καταχωρίσετε τους προεπιλεγμένους χρήστες με τους οποίους θέλετε " +"να μοιράζεστε συνταγές στις ρυθμίσεις." #: .\cookbook\forms.py:340 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" +"Μπορείτε να χρησιμοποιήσετε τη μορφοποίηση Markdown για να διαμορφώσετε αυτό " +"το πεδίο. Δείτε τα έγγραφα εδώ" #: .\cookbook\forms.py:366 msgid "Maximum number of users for this space reached." -msgstr "" +msgstr "Έχει επιτευχθεί ο μέγιστος αριθμός χρηστών για αυτόν τον χώρο." #: .\cookbook\forms.py:372 msgid "Email address already taken!" -msgstr "" +msgstr "Αυτή η διεύθυνση email δεν είναι διαθέσιμη!" #: .\cookbook\forms.py:380 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" +"Δεν απαιτείται η διεύθυνση email, αλλά αν υπάρχει, ο σύνδεσμος πρόσκλησης θα " +"αποσταλεί στον χρήστη." #: .\cookbook\forms.py:395 msgid "Name already taken." -msgstr "" +msgstr "Το όνομα αυτό είναι ήδη πιασμένο." #: .\cookbook\forms.py:406 msgid "Accept Terms and Privacy" -msgstr "" +msgstr "Αποδοχή των όρων και της πολιτικής απορρήτου" #: .\cookbook\forms.py:438 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" +"Καθορίζει πόσο ασαφής είναι η αναζήτηση εάν χρησιμοποιείται η αντιστοίχιση " +"ομοιότητας τριγώνων (trigram similarity matching) (π.χ. χαμηλές τιμές " +"σημαίνουν ότι αγνοούνται περισσότερα λάθη πληκτρολόγησης)." #: .\cookbook\forms.py:448 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" +"Επιλέξτε τη μέθοδο αναζήτησης. Κάντε κλικ εδώ " +"για πλήρη περιγραφή των επιλογών." #: .\cookbook\forms.py:449 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" +"Χρησιμοποιήστε ασαφείς (fuzzy) αντιστοιχίες σε μονάδες μέτρησης, λέξεις-" +"κλειδιά και συστατικά κατά την επεξεργασία και εισαγωγή συνταγών." #: .\cookbook\forms.py:451 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" +"Πεδία αναζήτησης αγνοώντας τις τόνους. Η επιλογή αυτή μπορεί να βελτιώσει " +"ή να επιδεινώσει την ποιότητα της αναζήτησης, ανάλογα με τη γλώσσα" #: .\cookbook\forms.py:453 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" +"Πεδία για αναζήτηση μερικών αντιστοιχιών. (π.χ. αναζήτηση για 'πίτα' τα " +"'τυρόπιτα' και 'απιτα' θα βρίσκονται στα αποτελέσματα)" #: .\cookbook\forms.py:455 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" +"Πεδία για αναζήτηση αρχής λέξεων. (π.χ. η αναζήτηση για το γράμμα 'σα' θα " +"επιστρέψει τις λέξεις 'σαλάτα' και 'σάντουιτς')" #: .\cookbook\forms.py:457 msgid "" @@ -306,7 +352,7 @@ msgstr "" #: .\cookbook\forms.py:463 msgid "Search Method" -msgstr "" +msgstr "Μέθοδος αναζήτησης" #: .\cookbook\forms.py:464 msgid "Fuzzy Lookups" @@ -314,101 +360,115 @@ msgstr "" #: .\cookbook\forms.py:465 msgid "Ignore Accent" -msgstr "" +msgstr "Αγνόηση τόνων" #: .\cookbook\forms.py:466 msgid "Partial Match" -msgstr "" +msgstr "Μερική ταύτιση" #: .\cookbook\forms.py:467 msgid "Starts With" -msgstr "" +msgstr "Ξεκινάει με" #: .\cookbook\forms.py:468 msgid "Fuzzy Search" -msgstr "" +msgstr "Ασαφής αναζήτηση(fuzzy)" #: .\cookbook\forms.py:469 msgid "Full Text" -msgstr "" +msgstr "Πλήρες κείμενο" #: .\cookbook\forms.py:494 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" +"Οι χρήστες θα μπορούν να δουν όλα τα αντικείμενα που προστίθενται στην λίστα " +"αγορών σας. Για να δείτε τα αντικείμενα στις λίστα αυτών θα πρέπει να σας " +"προστέσουν." #: .\cookbook\forms.py:500 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" +"Όταν προστίθεται ένα πρόγραμμα γευμάτων στη λίστα αγορών (χειροκίνητα ή " +"αυτόματα), να συμπεριλαμβάνονται όλες οι σχετικές συνταγές." #: .\cookbook\forms.py:501 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" +"Όταν προσθέτετε ένα προγραμματισμό γεύματος στη λίστα αγορών (χειροκίνητα ή " +"αυτόματα), αποκλείστε τα συστατικά που έχετε ήδη στη διάθεσή σας." #: .\cookbook\forms.py:502 msgid "Default number of hours to delay a shopping list entry." msgstr "" +"Προεπιλεγμένος αριθμός ωρών για την καθυστέρηση μιας εγγραφής στη λίστα " +"αγορών." #: .\cookbook\forms.py:503 msgid "Filter shopping list to only include supermarket categories." msgstr "" +"Φιλτράρισμα λίστας αγορών ώστε να περιλαμβάνει μόνο τις κατηγορίες του " +"supermarker." #: .\cookbook\forms.py:504 msgid "Days of recent shopping list entries to display." -msgstr "" +msgstr "Αριθμός ημερών για τη προβολή των πρόσφατων εγγραφών της λίστας αγορών." #: .\cookbook\forms.py:505 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" +"Χαρακτηρισμός ενός τροφίμου ως 'Διαθέσιμο' όταν τσεκαριστεί στη λίστα αγορών." #: .\cookbook\forms.py:506 msgid "Delimiter to use for CSV exports." -msgstr "" +msgstr "Το σημείο στίξης διαχωρισμού δεκαδικών για τις εξαγωγές σε αρχεία CSV." #: .\cookbook\forms.py:507 msgid "Prefix to add when copying list to the clipboard." msgstr "" +"Πρόθεμα που προστίθεται κατά την αντιγραφή της λίστας στο πρόχειρο " +"(clipboard)." #: .\cookbook\forms.py:511 msgid "Share Shopping List" -msgstr "" +msgstr "Κοινοποίηση λίστας αγορών" #: .\cookbook\forms.py:512 msgid "Autosync" -msgstr "" +msgstr "Αυτόματος συγχρονισμός" #: .\cookbook\forms.py:513 msgid "Auto Add Meal Plan" -msgstr "" +msgstr "Αυτόματη προσθήκη προγραμματισμού γευμάτων" #: .\cookbook\forms.py:514 msgid "Exclude On Hand" -msgstr "" +msgstr "Αποκλεισμός διαθέσιμων" #: .\cookbook\forms.py:515 msgid "Include Related" -msgstr "" +msgstr "Συμπερίληψη σχετικών" #: .\cookbook\forms.py:516 msgid "Default Delay Hours" -msgstr "" +msgstr "Προεπιλεγμένες ώρες καθυστέρησης" #: .\cookbook\forms.py:517 msgid "Filter to Supermarket" -msgstr "" +msgstr "Ταξινόμηση ανά Supermarket" #: .\cookbook\forms.py:518 msgid "Recent Days" -msgstr "" +msgstr "Πρόσφατες ημέρες" #: .\cookbook\forms.py:519 msgid "CSV Delimiter" -msgstr "" +msgstr "CSV σημείο στίξης διαχωρισμού δεκαδικών" #: .\cookbook\forms.py:520 msgid "List Prefix" @@ -416,11 +476,11 @@ msgstr "" #: .\cookbook\forms.py:521 msgid "Auto On Hand" -msgstr "" +msgstr "Αυτόματα διαθέσιμο" #: .\cookbook\forms.py:531 msgid "Reset Food Inheritance" -msgstr "" +msgstr "Επαναφορά κληρονομιάς φαγητών" #: .\cookbook\forms.py:532 msgid "Reset all food to inherit the fields configured." @@ -428,7 +488,7 @@ msgstr "" #: .\cookbook\forms.py:544 msgid "Fields on food that should be inherited by default." -msgstr "" +msgstr "Πεδία στα φαγητά που πρέπει να κληρονομούνται από προεπιλογή." #: .\cookbook\forms.py:545 msgid "Show recipe counts on search filters" @@ -439,11 +499,13 @@ msgid "" "In order to prevent spam, the requested email was not send. Please wait a " "few minutes and try again." msgstr "" +"Για να αποκλείσουμε πιθανά spam, το email που ζητήθηκε δεν στάλθηκε. " +"Παρακαλώ περιμένετε λίγα λεπτά και δοκιμάστε ξανά." #: .\cookbook\helper\permission_helper.py:149 #: .\cookbook\helper\permission_helper.py:172 .\cookbook\views\views.py:152 msgid "You are not logged in and therefore cannot view this page!" -msgstr "" +msgstr "Δεν μπορείτε να δείτε αυτή τη σελίδα γιατί δεν είστε συνδεδεμένος!" #: .\cookbook\helper\permission_helper.py:153 #: .\cookbook\helper\permission_helper.py:159 @@ -455,7 +517,7 @@ msgstr "" #: .\cookbook\views\views.py:163 .\cookbook\views\views.py:170 #: .\cookbook\views\views.py:249 msgid "You do not have the required permissions to view this page!" -msgstr "" +msgstr "Δεν έχετε τα απαιτούμενα δικαιώματα να δείτε αυτή τη σελίδα!" #: .\cookbook\helper\permission_helper.py:177 #: .\cookbook\helper\permission_helper.py:200 @@ -463,22 +525,23 @@ msgstr "" #: .\cookbook\helper\permission_helper.py:237 msgid "You cannot interact with this object as it is not owned by you!" msgstr "" +"Δεν μπορείτε να αλληλεπιδράστε με αυτό το αντικείμενο γιατί δεν σας ανήκει!" #: .\cookbook\helper\permission_helper.py:321 msgid "You have reached the maximum number of recipes for your space." -msgstr "" +msgstr "Έχετε υπερβεί τον μέγιστο αριθμό συνταγών για τον χώρο σας." #: .\cookbook\helper\permission_helper.py:333 msgid "You have more users than allowed in your space." -msgstr "" +msgstr "Έχετε περισσότερους χρήστες από το επιτρεπόμενο στον χώρο σας." #: .\cookbook\helper\recipe_search.py:565 msgid "One of queryset or hash_key must be provided" -msgstr "" +msgstr "Πρέπει να παρέχετε είτε το queryset είτε το hash_key" #: .\cookbook\helper\shopping_helper.py:152 msgid "You must supply a servings size" -msgstr "" +msgstr "Θα πρέπει να προσθέσετε το μέγεθος της μερίδας" #: .\cookbook\helper\template_helper.py:64 #: .\cookbook\helper\template_helper.py:66 @@ -488,150 +551,158 @@ msgstr "" #: .\cookbook\integration\copymethat.py:41 #: .\cookbook\integration\melarecipes.py:37 msgid "Favorite" -msgstr "" +msgstr "Αγαπημένα" #: .\cookbook\integration\copymethat.py:70 #: .\cookbook\integration\recettetek.py:54 #: .\cookbook\integration\recipekeeper.py:63 msgid "Imported from" -msgstr "" +msgstr "Εισήχθη από" #: .\cookbook\integration\integration.py:223 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" +"Ο εισαγωγέας περίμενε ένα αρχείο .zip. Έχετε σίγουρα διαλέξει τον σωστό τύπο " +"εισαγωγέα για τα δεδομένα θέλετε να εισάγετε;" #: .\cookbook\integration\integration.py:226 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" +"Παρουσιάστηκε ένα απρόβλεπτο σφάλμα κατά την εισαγωγή. Βεβαιωθείτε ότι έχετε " +"μεταφορτώσει ένα έγκυρο αρχείο." #: .\cookbook\integration\integration.py:231 msgid "The following recipes were ignored because they already existed:" -msgstr "" +msgstr "Οι παρακάτω συνταγές αγνοήθηκαν επειδή υπήρχαν ήδη:" #: .\cookbook\integration\integration.py:235 #, python-format msgid "Imported %s recipes." -msgstr "" +msgstr "Εισήχθησαν %s συνταγές." #: .\cookbook\integration\paprika.py:46 msgid "Notes" -msgstr "" +msgstr "Σημειώσεις" #: .\cookbook\integration\paprika.py:49 msgid "Nutritional Information" -msgstr "" +msgstr "Διατροφικές πληροφορίες" #: .\cookbook\integration\paprika.py:53 msgid "Source" -msgstr "" +msgstr "Πηγή" #: .\cookbook\integration\saffron.py:23 msgid "Servings" -msgstr "" +msgstr "Μερίδες" #: .\cookbook\integration\saffron.py:25 msgid "Waiting time" -msgstr "" +msgstr "Χρόνος αναμονής" #: .\cookbook\integration\saffron.py:27 msgid "Preparation Time" -msgstr "" +msgstr "Χρόνος προετοιμασίας" #: .\cookbook\integration\saffron.py:29 #: .\cookbook\templates\forms\ingredients.html:7 #: .\cookbook\templates\index.html:7 msgid "Cookbook" -msgstr "" +msgstr "Βιβλίο συνταγών" #: .\cookbook\integration\saffron.py:31 msgid "Section" -msgstr "" +msgstr "Τομέας" #: .\cookbook\management\commands\rebuildindex.py:14 msgid "Rebuilds full text search index on Recipe" -msgstr "" +msgstr "Αναδόμηση πλήρους ευρετηρίου αναζήτησης κειμένου για τις συνταγές" #: .\cookbook\management\commands\rebuildindex.py:18 msgid "Only Postgresql databases use full text search, no index to rebuild" msgstr "" +"Μόνο οι βάσεις δεδομένων Postgresql χρησιμοποιούν αναζήτηση πλήρους " +"κειμένου, δεν υπάρχει ανάγκη ανασύνθεσης των ευρετηρίων" #: .\cookbook\management\commands\rebuildindex.py:29 msgid "Recipe index rebuild complete." -msgstr "" +msgstr "Η αναδόμηση του ευρετηρίου των συνταγών ολοκληρώθηκε." #: .\cookbook\management\commands\rebuildindex.py:31 msgid "Recipe index rebuild failed." -msgstr "" +msgstr "Η αναδόμηση του ευρετηρίου των συνταγών απέτυχε." #: .\cookbook\migrations\0047_auto_20200602_1133.py:14 msgid "Breakfast" -msgstr "" +msgstr "Πρωινό" #: .\cookbook\migrations\0047_auto_20200602_1133.py:19 msgid "Lunch" -msgstr "" +msgstr "Μεσημεριανό" #: .\cookbook\migrations\0047_auto_20200602_1133.py:24 msgid "Dinner" -msgstr "" +msgstr "Βραδινό" #: .\cookbook\migrations\0047_auto_20200602_1133.py:29 msgid "Other" -msgstr "" +msgstr "Άλλο" #: .\cookbook\models.py:251 msgid "" "Maximum file storage for space in MB. 0 for unlimited, -1 to disable file " "upload." msgstr "" +"Μέγιστος χώρος αποθήκευσης αρχείων σε MB. Ορίστε το σε 0 για απεριόριστο " +"χώρο, σε -1 για να απενεργοποιήσετε τη μεταφόρτωση αρχείων." #: .\cookbook\models.py:353 .\cookbook\templates\search.html:7 #: .\cookbook\templates\space_manage.html:7 msgid "Search" -msgstr "" +msgstr "Αναζήτηση" #: .\cookbook\models.py:354 .\cookbook\templates\base.html:107 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" -msgstr "" +msgstr "Πραγματισμός γευμάτων" #: .\cookbook\models.py:355 .\cookbook\templates\base.html:115 msgid "Books" -msgstr "" +msgstr "Βιβλία" #: .\cookbook\models.py:363 msgid "Small" -msgstr "" +msgstr "Μικρό" #: .\cookbook\models.py:363 msgid "Large" -msgstr "" +msgstr "Μεγάλο" #: .\cookbook\models.py:363 .\cookbook\templates\generic\new_template.html:6 #: .\cookbook\templates\generic\new_template.html:14 msgid "New" -msgstr "" +msgstr "Νέο" #: .\cookbook\models.py:584 msgid " is part of a recipe step and cannot be deleted" -msgstr "" +msgstr " είναι μέρος ενός βήματος συνταγής και δεν μπορεί να διαγράφει" #: .\cookbook\models.py:1162 .\cookbook\templates\search_info.html:28 msgid "Simple" -msgstr "" +msgstr "Απλό" #: .\cookbook\models.py:1163 .\cookbook\templates\search_info.html:33 msgid "Phrase" -msgstr "" +msgstr "Φράση" #: .\cookbook\models.py:1164 .\cookbook\templates\search_info.html:38 msgid "Web" -msgstr "" +msgstr "Δίκτυο" #: .\cookbook\models.py:1165 .\cookbook\templates\search_info.html:47 msgid "Raw" @@ -654,71 +725,78 @@ msgstr "" #: .\cookbook\views\delete.py:36 .\cookbook\views\edit.py:251 #: .\cookbook\views\new.py:48 msgid "Recipe" -msgstr "" +msgstr "Συνταγή" #: .\cookbook\models.py:1228 msgid "Food" -msgstr "" +msgstr "Φαγητό" #: .\cookbook\models.py:1229 .\cookbook\templates\base.html:138 msgid "Keyword" -msgstr "" +msgstr "Λέξη κλειδί" #: .\cookbook\serializer.py:207 msgid "Cannot modify Space owner permission." -msgstr "" +msgstr "Δεν είναι δυνατή η τροποποίηση των δικαιωμάτων του ιδιοκτήτη του χώρου." #: .\cookbook\serializer.py:290 msgid "File uploads are not enabled for this Space." -msgstr "" +msgstr "Οι μεταφορτώσεις αρχείων δεν είναι ενεργοποιημένες για αυτόν τον χώρο." #: .\cookbook\serializer.py:301 msgid "You have reached your file upload limit." -msgstr "" +msgstr "Έχετε φτάσει το όριο μεταφόρτωσης αρχείων." #: .\cookbook\serializer.py:1081 msgid "Hello" -msgstr "" +msgstr "Γεια" #: .\cookbook\serializer.py:1081 msgid "You have been invited by " -msgstr "" +msgstr "Έχετε προσκληθεί από " #: .\cookbook\serializer.py:1082 msgid " to join their Tandoor Recipes space " -msgstr "" +msgstr " για να συνδεθείτε στό χώρο συνταγών του Tandoor " #: .\cookbook\serializer.py:1083 msgid "Click the following link to activate your account: " msgstr "" +"Κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε τον λογαριασμό σας: " #: .\cookbook\serializer.py:1084 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" +"Εάν ο σύνδεσμος δεν λειτουργεί, χρησιμοποιήστε τον παρακάτω κωδικό για να " +"εγγραφείτε χειροκίνητα στον χώρο: " #: .\cookbook\serializer.py:1085 msgid "The invitation is valid until " -msgstr "" +msgstr "Η πρόσκληση είναι σε ισχύ μέχρι " #: .\cookbook\serializer.py:1086 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" +"Το Tandoor Recipes είναι ένας διαχειριστής συνταγών ανοιχτού κώδικα. Ρίξτε " +"μια ματιά στο GitHub " #: .\cookbook\serializer.py:1089 msgid "Tandoor Recipes Invite" -msgstr "" +msgstr "Πρόσκληση στο Tandoor Recipes" #: .\cookbook\serializer.py:1209 msgid "Existing shopping list to update" -msgstr "" +msgstr "Υπάρχουσα λίστα αγορών για ενημέρωση" #: .\cookbook\serializer.py:1211 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" +"Λίστα αναγνωριστικών συστατικών (ID) από τη συνταγή προς προσθήκη. Εάν δεν " +"παρέχονται όλα τα συστατικά θα προστεθούν." #: .\cookbook\serializer.py:1213 msgid "" @@ -727,21 +805,23 @@ msgstr "" #: .\cookbook\serializer.py:1222 msgid "Amount of food to add to the shopping list" -msgstr "" +msgstr "Ποσότητα του φαγητού που θα προστεθεί στη λίστα αγορών" #: .\cookbook\serializer.py:1224 msgid "ID of unit to use for the shopping list" -msgstr "" +msgstr "Το ID της μονάδας μέτρησης που θα χρησιμοποιείται στη λίστα αγορών" #: .\cookbook\serializer.py:1226 msgid "When set to true will delete all food from active shopping lists." msgstr "" +"Όταν οριστεί σε true, θα διαγραφούν όλα τα τρόφιμα από τις ενεργές λίστες " +"αγορών." #: .\cookbook\tables.py:36 .\cookbook\templates\generic\edit_template.html:6 #: .\cookbook\templates\generic\edit_template.html:14 #: .\cookbook\templates\recipes_table.html:82 msgid "Edit" -msgstr "" +msgstr "Τροποποίηση" #: .\cookbook\tables.py:116 .\cookbook\tables.py:131 #: .\cookbook\templates\generic\delete_template.html:7 @@ -749,28 +829,28 @@ msgstr "" #: .\cookbook\templates\generic\edit_template.html:28 #: .\cookbook\templates\recipes_table.html:90 msgid "Delete" -msgstr "" +msgstr "Διαγραφή" #: .\cookbook\templates\404.html:5 msgid "404 Error" -msgstr "" +msgstr "404 Error" #: .\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 "Διευθύνσεις e-mail" #: .\cookbook\templates\account\email.html:12 #: .\cookbook\templates\account\password_change.html:11 @@ -779,68 +859,71 @@ msgstr "" #: .\cookbook\templates\settings.html:17 #: .\cookbook\templates\socialaccount\connections.html:10 msgid "Settings" -msgstr "" +msgstr "Ρυθμίσεις" #: .\cookbook\templates\account\email.html:13 msgid "Email" -msgstr "" +msgstr "Email" #: .\cookbook\templates\account\email.html:19 msgid "The following e-mail addresses are associated with your account:" -msgstr "" +msgstr "Οι παρακάτω διευθύνσεις e-mail συνδέονται με τον λογαριασμό σας:" #: .\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 "" +"Προς το παρόν, δεν έχετε καμία διεύθυνση e-mail καταχωρημένη. Θα πρέπει να " +"προσθέσετε μια διεύθυνση ηλεκτρονικού ταχυδρομείου, ώστε να μπορείτε να " +"λαμβάνετε ειδοποιήσεις, να επαναφέρετε τον κωδικό πρόσβασης, κ.λπ." #: .\cookbook\templates\account\email.html:64 msgid "Add E-mail Address" -msgstr "" +msgstr "Προσθήκη διεύθυνσης e-mail" #: .\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 "Θέλετε πραγματικά να αφαιρέσετε την επιλεγμένη διεύθυνση e-mail;" #: .\cookbook\templates\account\email_confirm.html:6 #: .\cookbook\templates\account\email_confirm.html:10 msgid "Confirm E-mail Address" -msgstr "" +msgstr "Επιβεβαίωση διεύθυνσης e-mail" #: .\cookbook\templates\account\email_confirm.html:16 #, python-format @@ -850,11 +933,15 @@ msgid "" "for user %(user_display)s\n" " ." msgstr "" +"Παρακαλώ επιβεβαιώστε ότι το\n" +" %(email)s είναι μια διεύθυνση " +"e-mail για τον χρήστη %(user_display)s\n" +" ." #: .\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 @@ -863,11 +950,15 @@ msgid "" " issue a new e-mail confirmation " "request." msgstr "" +"Αυτός ο σύνδεσμος επιβεβαίωσης έχει λήξει είναι δεν είναι έγκυρος. Παρακαλώ " +"\n" +" κάντε ένα νέο αίτημα για επιβεβαιωτικό " +"e-mail." #: .\cookbook\templates\account\login.html:8 #: .\cookbook\templates\base.html:340 .\cookbook\templates\openid\login.html:8 msgid "Login" -msgstr "" +msgstr "Σύνδεση" #: .\cookbook\templates\account\login.html:15 #: .\cookbook\templates\account\login.html:31 @@ -877,41 +968,43 @@ 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\socialaccount\signup.html:8 #: .\cookbook\templates\socialaccount\signup.html:57 msgid "Sign Up" -msgstr "" +msgstr "Εγγραφή" #: .\cookbook\templates\account\login.html:39 #: .\cookbook\templates\account\login.html:41 #: .\cookbook\templates\account\password_reset.html:29 msgid "Reset My Password" -msgstr "" +msgstr "Επαναφορά κωδικού πρόσβασης" #: .\cookbook\templates\account\login.html:40 msgid "Lost your password?" -msgstr "" +msgstr "Χασάτε τον κωδικό πρόσβασης;" #: .\cookbook\templates\account\login.html:52 msgid "Social Login" -msgstr "" +msgstr "Σύνδεση με social media" #: .\cookbook\templates\account\login.html:53 msgid "You can use any of the following providers to sign in." msgstr "" +"Μπορείτε να χρησιμοποιήσετε οποιονδήποτε από τους παρακάτω παρόχους για να " +"συνδεθείτε." #: .\cookbook\templates\account\logout.html:5 #: .\cookbook\templates\account\logout.html:9 #: .\cookbook\templates\account\logout.html:18 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 @@ -921,44 +1014,50 @@ 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 #: .\cookbook\templates\settings.html:76 msgid "Password" -msgstr "" +msgstr "Κωδικός πρόσβασης" #: .\cookbook\templates\account\password_change.html:22 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:10 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 "" +"Ξεχάσατε τον κωδικό πρόσβασης σας; Εισάγετε τη διεύθυνση ηλεκτρονικού " +"ταχυδρομείου σας παρακάτω και θα σας στείλουμε ένα email που θα σας " +"επιτρέψει να τον επαναφέρετε." #: .\cookbook\templates\account\password_reset.html:32 msgid "Password reset is disabled on this instance." msgstr "" +"Η επαναφορά κωδικού πρόσβασης είναι απενεργοποιημένη σε αυτήν την πλατφόρμα." #: .\cookbook\templates\account\password_reset_done.html:16 msgid "" "We have sent you an e-mail. Please contact us if you do not receive it " "within a few minutes." msgstr "" +"Σας έχουμε στείλει ένα email. Παρακαλούμε επικοινωνήστε μαζί μας αν δεν το " +"λάβετε εντός λίγων λεπτών." #: .\cookbook\templates\account\password_reset_from_key.html:13 msgid "Bad Token" -msgstr "" +msgstr "Μη έγκυρο token" #: .\cookbook\templates\account\password_reset_from_key.html:25 #, python-format @@ -968,222 +1067,230 @@ msgid "" " Please request a new " "password reset." msgstr "" +"Ο σύνδεσμος επαναφοράς κωδικού πρόσβασης ήταν άκυρος, πιθανώς επειδή έχει " +"ήδη χρησιμοποιηθεί.\n" +" Παρακαλώ ζητήστε έναν νέο σύνδεσμο επαναφοράς κωδικού πρόσβασης." #: .\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 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:330 #: .\cookbook\templates\rest_framework\api.html:11 msgid "API Documentation" -msgstr "" +msgstr "Τεκμηρίωση API" #: .\cookbook\templates\base.html:103 .\cookbook\templates\index.html:87 #: .\cookbook\templates\stats.html:22 msgid "Recipes" -msgstr "" +msgstr "Συνταγές" #: .\cookbook\templates\base.html:111 msgid "Shopping" -msgstr "" +msgstr "Αγορές" #: .\cookbook\templates\base.html:150 .\cookbook\views\lists.py:105 msgid "Foods" -msgstr "" +msgstr "Φαγητά" #: .\cookbook\templates\base.html:162 #: .\cookbook\templates\forms\ingredients.html:24 #: .\cookbook\templates\stats.html:26 .\cookbook\views\lists.py:122 msgid "Units" -msgstr "" +msgstr "Μονάδες μέτρησης" #: .\cookbook\templates\base.html:176 .\cookbook\templates\supermarket.html:7 msgid "Supermarket" -msgstr "" +msgstr "Supermarket" #: .\cookbook\templates\base.html:188 msgid "Supermarket Category" -msgstr "" +msgstr "Κατηγορία Supermarket" #: .\cookbook\templates\base.html:200 .\cookbook\views\lists.py:171 msgid "Automations" -msgstr "" +msgstr "Αυτοματισμοί" #: .\cookbook\templates\base.html:214 .\cookbook\views\lists.py:207 msgid "Files" -msgstr "" +msgstr "Αρχεία" #: .\cookbook\templates\base.html:226 msgid "Batch Edit" -msgstr "" +msgstr "Μαζική Επεξεργασία" #: .\cookbook\templates\base.html:238 .\cookbook\templates\history.html:6 #: .\cookbook\templates\history.html:14 msgid "History" -msgstr "" +msgstr "Ιστορικό" #: .\cookbook\templates\base.html:252 #: .\cookbook\templates\ingredient_editor.html:7 #: .\cookbook\templates\ingredient_editor.html:13 msgid "Ingredient Editor" -msgstr "" +msgstr "Επεξεργαστής Συστατικών" #: .\cookbook\templates\base.html:264 #: .\cookbook\templates\export_response.html:7 #: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20 msgid "Export" -msgstr "" +msgstr "Εξαγωγή" #: .\cookbook\templates\base.html:280 .\cookbook\templates\index.html:47 msgid "Import Recipe" -msgstr "" +msgstr "Εισαγωγή συνταγής" #: .\cookbook\templates\base.html:282 msgid "Create" -msgstr "" +msgstr "Δημιουργία" #: .\cookbook\templates\base.html:295 #: .\cookbook\templates\generic\list_template.html:14 #: .\cookbook\templates\stats.html:43 msgid "External Recipes" -msgstr "" +msgstr "Εξωτερικές Συνταγές" #: .\cookbook\templates\base.html:298 #: .\cookbook\templates\space_manage.html:15 msgid "Space Settings" -msgstr "" +msgstr "Ρυθμίσεις χώρου" #: .\cookbook\templates\base.html:303 .\cookbook\templates\system.html:13 msgid "System" -msgstr "" +msgstr "Σύστημα" #: .\cookbook\templates\base.html:305 msgid "Admin" -msgstr "" +msgstr "Διαχειριστής" #: .\cookbook\templates\base.html:309 #: .\cookbook\templates\space_overview.html:25 msgid "Your Spaces" -msgstr "" +msgstr "Οι χώροι σας" #: .\cookbook\templates\base.html:320 #: .\cookbook\templates\space_overview.html:6 msgid "Overview" -msgstr "" +msgstr "Σύνοψη" #: .\cookbook\templates\base.html:324 msgid "Markdown Guide" -msgstr "" +msgstr "Οδηγός χρήσης του Markdown" #: .\cookbook\templates\base.html:326 msgid "GitHub" -msgstr "" +msgstr "GitHub" #: .\cookbook\templates\base.html:328 msgid "Translate Tandoor" -msgstr "" +msgstr "Μεταφράστε το Tandoor" #: .\cookbook\templates\base.html:332 msgid "API Browser" -msgstr "" +msgstr "Περιηγητής API" #: .\cookbook\templates\base.html:335 msgid "Log out" -msgstr "" +msgstr "Αποσύνδεση" #: .\cookbook\templates\base.html:357 msgid "You are using the free version of Tandor" -msgstr "" +msgstr "Χρησιμοποιείται την δωρεάν έκδοση του Tandoor" #: .\cookbook\templates\base.html:358 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 "" +"Προσθέστε τις καθορισμένες λέξεις-κλειδιά σε όλες τις συνταγές που περιέχουν " +"μια λέξη" #: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:73 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 "Η διαδρομή (path) πρέπει να είναι στην ακόλουθη μορφή" #: .\cookbook\templates\batch\monitor.html:20 #: .\cookbook\templates\forms\edit_import_recipe.html:14 @@ -1195,55 +1302,57 @@ msgstr "" #: .\cookbook\templates\settings.html:202 #: .\cookbook\templates\settings.html:213 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:8 .\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\forms\ingredients.html:15 msgid "Edit Ingredients" -msgstr "" +msgstr "Τροποποίηση υλικών" #: .\cookbook\templates\forms\ingredients.html:16 msgid "" @@ -1255,32 +1364,41 @@ msgid "" "them.\n" " " msgstr "" +"\n" +" Την παρακάτω φόρμα μπορεί να χρησιμοποιηθεί στην περίπτωση που, κατά " +"λάθος, δημιουργήθηκαν δύο (ή περισσότερες) μονάδες μέτρησης ή συστατικά που " +"θα έπρεπε να είναι\n" +" τα ίδια.\n" +" Αυτή η φόρμα συγχωνεύει δύο μονάδες ή συστατικά και ενημερώνει όλες " +"τις συνταγές που τα χρησιμοποιούν.\n" +" " #: .\cookbook\templates\forms\ingredients.html:26 msgid "Are you sure that you want to merge these two units?" -msgstr "" +msgstr "Είστε βέβαιος ότι θέλετε να συγχωνεύσετε αυτές τις δύο μονάδες;" #: .\cookbook\templates\forms\ingredients.html:31 #: .\cookbook\templates\forms\ingredients.html:40 msgid "Merge" -msgstr "" +msgstr "Συγχώνευση" #: .\cookbook\templates\forms\ingredients.html:36 msgid "Are you sure that you want to merge these two ingredients?" -msgstr "" +msgstr "Είστε βέβαιος ότι θέλετε να συγχωνεύσετε αυτά τα δύο υλικά;" #: .\cookbook\templates\generic\delete_template.html:21 #, python-format msgid "Are you sure you want to delete the %(title)s: %(object)s " msgstr "" +"Είστε σίγουροι ότι θέλετε να διαγράψετε τα %(title)s: %(object)s " #: .\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" @@ -1288,68 +1406,68 @@ msgstr "" #: .\cookbook\templates\generic\delete_template.html:73 msgid "Cancel" -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\table_template.html:76 #: .\cookbook\templates\recipes_table.html:121 msgid "previous" -msgstr "" +msgstr "προηγούμενο" #: .\cookbook\templates\generic\table_template.html:98 #: .\cookbook\templates\recipes_table.html:143 msgid "next" -msgstr "" +msgstr "επόμενο" #: .\cookbook\templates\history.html:20 msgid "View Log" -msgstr "" +msgstr "Προβολή αρχείων καταγραφής" #: .\cookbook\templates\history.html:24 msgid "Cook Log" -msgstr "" +msgstr "Αρχείο καταγραφής μαγειρέματος" #: .\cookbook\templates\import.html:6 msgid "Import Recipes" -msgstr "" +msgstr "Εισαγωγή Συνταγών" #: .\cookbook\templates\import.html:14 .\cookbook\templates\import.html:20 #: .\cookbook\templates\import_response.html:7 .\cookbook\views\delete.py:86 #: .\cookbook\views\edit.py:191 msgid "Import" -msgstr "" +msgstr "Εισαγωγή" #: .\cookbook\templates\include\recipe_open_modal.html:18 msgid "Close" -msgstr "" +msgstr "Κλείσιμο" #: .\cookbook\templates\include\recipe_open_modal.html:32 msgid "Open Recipe" -msgstr "" +msgstr "Άνοιγμα Συνταγής" #: .\cookbook\templates\include\storage_backend_warning.html:4 msgid "Security Warning" -msgstr "" +msgstr "Προειδοποίηση ασφαλείας" #: .\cookbook\templates\include\storage_backend_warning.html:5 msgid "" @@ -1366,32 +1484,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 "" @@ -1411,31 +1529,33 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:25 msgid "Headers" -msgstr "" +msgstr "Επικεφαλίδες" #: .\cookbook\templates\markdown_info.html:54 msgid "Formatting" -msgstr "" +msgstr "Μορφοποίηση" #: .\cookbook\templates\markdown_info.html:56 #: .\cookbook\templates\markdown_info.html:72 msgid "Line breaks are inserted by adding two spaces after the end of a line" msgstr "" +"Οι αλλαγές γραμμής εισάγονται προσθέτοντας δύο κενά μετά το τέλος μιας " +"γραμμής" #: .\cookbook\templates\markdown_info.html:57 #: .\cookbook\templates\markdown_info.html:73 msgid "or by leaving a blank line in between." -msgstr "" +msgstr "ή αφήνοντας μια κενή γραμμή μεταξύ τους." #: .\cookbook\templates\markdown_info.html:59 #: .\cookbook\templates\markdown_info.html:74 msgid "This text is bold" -msgstr "" +msgstr "Το κείμενο είναι έντονο (bold)" #: .\cookbook\templates\markdown_info.html:60 #: .\cookbook\templates\markdown_info.html:75 msgid "This text is italic" -msgstr "" +msgstr "Αυτό το κείμενο είναι πλάγιο (italic)" #: .\cookbook\templates\markdown_info.html:61 #: .\cookbook\templates\markdown_info.html:77 @@ -1444,7 +1564,7 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:84 msgid "Lists" -msgstr "" +msgstr "Λίστες" #: .\cookbook\templates\markdown_info.html:85 msgid "" @@ -1482,7 +1602,7 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:125 msgid "Images & Links" -msgstr "" +msgstr "Φωτογραφίες και σύνδεσμοι" #: .\cookbook\templates\markdown_info.html:126 msgid "" @@ -2608,3 +2728,5 @@ msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." msgstr "" +"Ο σύνδεσμος κοινοποίησης συνταγής έχει απενεργοποιηθεί! Για περαιτέρω " +"πληροφορίες, παρακαλώ επικοινωνήστε με τον διαχειριστή της σελίδας." diff --git a/cookbook/locale/en/LC_MESSAGES/django.po b/cookbook/locale/en/LC_MESSAGES/django.po index 21b6b9356..a5abded59 100644 --- a/cookbook/locale/en/LC_MESSAGES/django.po +++ b/cookbook/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-08-29 13:09+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,91 +18,95 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: .\cookbook\forms.py:52 +#: .\cookbook\forms.py:53 msgid "Default unit" msgstr "" -#: .\cookbook\forms.py:53 +#: .\cookbook\forms.py:54 msgid "Use fractions" msgstr "" -#: .\cookbook\forms.py:54 +#: .\cookbook\forms.py:55 msgid "Use KJ" msgstr "" -#: .\cookbook\forms.py:55 +#: .\cookbook\forms.py:56 msgid "Theme" msgstr "" -#: .\cookbook\forms.py:56 +#: .\cookbook\forms.py:57 msgid "Navbar color" msgstr "" -#: .\cookbook\forms.py:57 +#: .\cookbook\forms.py:58 msgid "Sticky navbar" msgstr "" -#: .\cookbook\forms.py:58 +#: .\cookbook\forms.py:59 msgid "Default page" msgstr "" -#: .\cookbook\forms.py:59 +#: .\cookbook\forms.py:60 msgid "Plan sharing" msgstr "" -#: .\cookbook\forms.py:60 +#: .\cookbook\forms.py:61 msgid "Ingredient decimal places" msgstr "" -#: .\cookbook\forms.py:61 +#: .\cookbook\forms.py:62 msgid "Shopping list auto sync period" msgstr "" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:63 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "" -#: .\cookbook\forms.py:63 +#: .\cookbook\forms.py:64 msgid "Left-handed mode" msgstr "" -#: .\cookbook\forms.py:67 +#: .\cookbook\forms.py:65 +msgid "Show step ingredients table" +msgstr "" + +#: .\cookbook\forms.py:69 msgid "" "Color of the top navigation bar. Not all colors work with all themes, just " "try them out!" msgstr "" -#: .\cookbook\forms.py:69 +#: .\cookbook\forms.py:71 msgid "Default Unit to be used when inserting a new ingredient into a recipe." msgstr "" -#: .\cookbook\forms.py:71 +#: .\cookbook\forms.py:73 msgid "" "Enables support for fractions in ingredient amounts (e.g. convert decimals " "to fractions automatically)" msgstr "" -#: .\cookbook\forms.py:73 +#: .\cookbook\forms.py:75 msgid "Display nutritional energy amounts in joules instead of calories" msgstr "" -#: .\cookbook\forms.py:74 +#: .\cookbook\forms.py:76 msgid "Users with whom newly created meal plans should be shared by default." msgstr "" -#: .\cookbook\forms.py:75 +#: .\cookbook\forms.py:77 msgid "Users with whom to share shopping lists." msgstr "" -#: .\cookbook\forms.py:76 +#: .\cookbook\forms.py:78 msgid "Number of decimals to round ingredients." msgstr "" -#: .\cookbook\forms.py:77 +#: .\cookbook\forms.py:79 msgid "If you want to be able to create and see comments underneath recipes." msgstr "" -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:81 .\cookbook\forms.py:512 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -110,311 +114,318 @@ msgid "" "mobile data. If lower than instance limit it is reset when saving." msgstr "" -#: .\cookbook\forms.py:82 +#: .\cookbook\forms.py:84 msgid "Makes the navbar stick to the top of the page." msgstr "" -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:85 .\cookbook\forms.py:515 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" -#: .\cookbook\forms.py:84 +#: .\cookbook\forms.py:86 msgid "Exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:85 +#: .\cookbook\forms.py:87 msgid "Will optimize the UI for use with your left hand." msgstr "" -#: .\cookbook\forms.py:102 +#: .\cookbook\forms.py:88 +msgid "" +"Add ingredients table next to recipe steps. Applies at creation time for " +"manually created and URL imported recipes. Individual steps can be " +"overridden in the edit recipe view." +msgstr "" + +#: .\cookbook\forms.py:105 msgid "" "Both fields are optional. If none are given the username will be displayed " "instead" msgstr "" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:126 .\cookbook\forms.py:317 msgid "Name" msgstr "" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:318 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "" -#: .\cookbook\forms.py:125 +#: .\cookbook\forms.py:128 msgid "Preparation time in minutes" msgstr "" -#: .\cookbook\forms.py:126 +#: .\cookbook\forms.py:129 msgid "Waiting time (cooking/baking) in minutes" msgstr "" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:130 .\cookbook\forms.py:286 .\cookbook\forms.py:319 msgid "Path" msgstr "" -#: .\cookbook\forms.py:128 +#: .\cookbook\forms.py:131 msgid "Storage UID" msgstr "" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:164 msgid "Default" msgstr "" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:193 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." msgstr "" -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:216 msgid "Add your comment: " msgstr "" -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:231 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:238 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:247 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" msgstr "" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:285 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:287 msgid "Active" msgstr "" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:293 msgid "Search String" msgstr "" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:320 msgid "File ID" msgstr "" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:342 msgid "You must provide at least a recipe or a title." msgstr "" -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:355 msgid "You can list default users to share recipes with in the settings." msgstr "" -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:356 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:382 msgid "Maximum number of users for this space reached." msgstr "" -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:388 msgid "Email address already taken!" msgstr "" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:396 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:411 msgid "Name already taken." msgstr "" -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:422 msgid "Accept Terms and Privacy" msgstr "" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:454 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:464 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:465 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:467 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:469 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:471 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:473 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:475 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:479 msgid "Search Method" msgstr "" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:480 msgid "Fuzzy Lookups" msgstr "" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:481 msgid "Ignore Accent" msgstr "" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:482 msgid "Partial Match" msgstr "" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:483 msgid "Starts With" msgstr "" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:484 msgid "Fuzzy Search" msgstr "" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:485 msgid "Full Text" msgstr "" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:510 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:516 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:517 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:518 msgid "Default number of hours to delay a shopping list entry." msgstr "" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:519 msgid "Filter shopping list to only include supermarket categories." msgstr "" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:520 msgid "Days of recent shopping list entries to display." msgstr "" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:521 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:522 msgid "Delimiter to use for CSV exports." msgstr "" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:523 msgid "Prefix to add when copying list to the clipboard." msgstr "" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:527 msgid "Share Shopping List" msgstr "" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:528 msgid "Autosync" msgstr "" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:529 msgid "Auto Add Meal Plan" msgstr "" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:530 msgid "Exclude On Hand" msgstr "" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:531 msgid "Include Related" msgstr "" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:532 msgid "Default Delay Hours" msgstr "" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:533 msgid "Filter to Supermarket" msgstr "" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:534 msgid "Recent Days" msgstr "" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:535 msgid "CSV Delimiter" msgstr "" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:536 msgid "List Prefix" msgstr "" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:537 msgid "Auto On Hand" msgstr "" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:547 msgid "Reset Food Inheritance" msgstr "" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:548 msgid "Reset all food to inherit the fields configured." msgstr "" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:560 msgid "Fields on food that should be inherited by default." msgstr "" -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:561 msgid "Show recipe counts on search filters" msgstr "" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:562 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -425,7 +436,7 @@ msgid "" msgstr "" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:113 msgid "You are not logged in and therefore cannot view this page!" msgstr "" @@ -438,7 +449,7 @@ msgstr "" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:124 .\cookbook\views\views.py:131 msgid "You do not have the required permissions to view this page!" msgstr "" @@ -457,16 +468,44 @@ msgstr "" msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:632 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:316 +msgid "reverse rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:317 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:318 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:319 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:320 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:321 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:322 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "" -#: .\cookbook\helper\template_helper.py:79 -#: .\cookbook\helper\template_helper.py:81 +#: .\cookbook\helper\template_helper.py:90 +#: .\cookbook\helper\template_helper.py:92 msgid "Could not parse template code." msgstr "" @@ -479,36 +518,40 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "" -#: .\cookbook\integration\paprika.py:46 -msgid "Notes" +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" msgstr "" #: .\cookbook\integration\paprika.py:49 +msgid "Notes" +msgstr "" + +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "" @@ -565,155 +608,205 @@ msgstr "" msgid "Dinner" msgstr "" -#: .\cookbook\migrations\0047_auto_20200602_1133.py:29 +#: .\cookbook\migrations\0047_auto_20200602_1133.py:29 .\cookbook\models.py:774 msgid "Other" msgstr "" -#: .\cookbook\models.py:261 +#: .\cookbook\migrations\0190_auto_20230525_1506.py:17 +msgid "Fat" +msgstr "" + +#: .\cookbook\migrations\0190_auto_20230525_1506.py:17 +#: .\cookbook\migrations\0190_auto_20230525_1506.py:18 +#: .\cookbook\migrations\0190_auto_20230525_1506.py:19 +msgid "g" +msgstr "" + +#: .\cookbook\migrations\0190_auto_20230525_1506.py:18 +msgid "Carbohydrates" +msgstr "" + +#: .\cookbook\migrations\0190_auto_20230525_1506.py:19 +msgid "Proteins" +msgstr "" + +#: .\cookbook\migrations\0190_auto_20230525_1506.py:20 +msgid "Calories" +msgstr "" + +#: .\cookbook\migrations\0190_auto_20230525_1506.py:20 +msgid "kcal" +msgstr "" + +#: .\cookbook\models.py:264 msgid "" "Maximum file storage for space in MB. 0 for unlimited, -1 to disable file " "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:372 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:373 .\cookbook\templates\base.html:112 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:374 .\cookbook\templates\base.html:120 msgid "Books" msgstr "" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:611 msgid " is part of a recipe step and cannot be deleted" msgstr "" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:773 +msgid "Nutrition" +msgstr "" + +#: .\cookbook\models.py:773 +msgid "Allergen" +msgstr "" + +#: .\cookbook\models.py:774 +msgid "Price" +msgstr "" + +#: .\cookbook\models.py:774 +msgid "Goal" +msgstr "" + +#: .\cookbook\models.py:1275 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1276 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1277 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1278 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1327 msgid "Food Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1327 msgid "Unit Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1327 msgid "Keyword Alias" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1328 msgid "Description Replace" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1328 msgid "Instruction Replace" msgstr "" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1329 +msgid "Never Unit" +msgstr "" + +#: .\cookbook\models.py:1329 +msgid "Transpose Words" +msgstr "" + +#: .\cookbook\models.py:1355 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1356 msgid "Food" msgstr "" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1357 .\cookbook\templates\base.html:147 msgid "Keyword" msgstr "" -#: .\cookbook\serializer.py:198 +#: .\cookbook\serializer.py:222 msgid "File uploads are not enabled for this Space." msgstr "" -#: .\cookbook\serializer.py:209 +#: .\cookbook\serializer.py:233 msgid "You have reached your file upload limit." msgstr "" -#: .\cookbook\serializer.py:291 +#: .\cookbook\serializer.py:318 msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1233 msgid "Hello" msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1233 msgid "You have been invited by " msgstr "" -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1235 msgid " to join their Tandoor Recipes space " msgstr "" -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1237 msgid "Click the following link to activate your account: " msgstr "" -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1239 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1241 msgid "The invitation is valid until " msgstr "" -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1243 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1246 msgid "Tandoor Recipes Invite" msgstr "" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1388 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1390 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1392 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1401 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1403 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1405 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -748,7 +841,7 @@ msgstr "" #: .\cookbook\templates\account\email.html:12 #: .\cookbook\templates\account\password_change.html:11 #: .\cookbook\templates\account\password_set.html:11 -#: .\cookbook\templates\base.html:296 .\cookbook\templates\settings.html:6 +#: .\cookbook\templates\base.html:329 .\cookbook\templates\settings.html:6 #: .\cookbook\templates\settings.html:17 #: .\cookbook\templates\socialaccount\connections.html:10 #: .\cookbook\templates\user_settings.html:8 @@ -838,7 +931,7 @@ msgid "" "request." msgstr "" -#: .\cookbook\templates\account\login.html:8 .\cookbook\templates\base.html:343 +#: .\cookbook\templates\account\login.html:8 .\cookbook\templates\base.html:382 #: .\cookbook\templates\openid\login.html:8 msgid "Login" msgstr "" @@ -1001,124 +1094,132 @@ msgstr "" msgid "We are sorry, but the sign up is currently closed." msgstr "" -#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:333 +#: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:372 #: .\cookbook\templates\rest_framework\api.html:11 msgid "API Documentation" msgstr "" -#: .\cookbook\templates\base.html:106 .\cookbook\templates\index.html:87 +#: .\cookbook\templates\base.html:108 .\cookbook\templates\index.html:87 msgid "Recipes" msgstr "" -#: .\cookbook\templates\base.html:114 +#: .\cookbook\templates\base.html:116 msgid "Shopping" msgstr "" -#: .\cookbook\templates\base.html:153 .\cookbook\views\lists.py:105 +#: .\cookbook\templates\base.html:159 .\cookbook\views\lists.py:105 msgid "Foods" msgstr "" -#: .\cookbook\templates\base.html:165 .\cookbook\views\lists.py:122 +#: .\cookbook\templates\base.html:171 .\cookbook\views\lists.py:122 msgid "Units" msgstr "" -#: .\cookbook\templates\base.html:179 .\cookbook\templates\supermarket.html:7 +#: .\cookbook\templates\base.html:185 .\cookbook\templates\supermarket.html:7 msgid "Supermarket" msgstr "" -#: .\cookbook\templates\base.html:191 +#: .\cookbook\templates\base.html:197 msgid "Supermarket Category" msgstr "" -#: .\cookbook\templates\base.html:203 .\cookbook\views\lists.py:171 +#: .\cookbook\templates\base.html:209 .\cookbook\views\lists.py:171 msgid "Automations" msgstr "" -#: .\cookbook\templates\base.html:217 .\cookbook\views\lists.py:207 +#: .\cookbook\templates\base.html:223 .\cookbook\views\lists.py:207 msgid "Files" msgstr "" -#: .\cookbook\templates\base.html:229 +#: .\cookbook\templates\base.html:235 msgid "Batch Edit" msgstr "" -#: .\cookbook\templates\base.html:241 .\cookbook\templates\history.html:6 +#: .\cookbook\templates\base.html:247 .\cookbook\templates\history.html:6 #: .\cookbook\templates\history.html:14 msgid "History" msgstr "" -#: .\cookbook\templates\base.html:255 +#: .\cookbook\templates\base.html:261 #: .\cookbook\templates\ingredient_editor.html:7 #: .\cookbook\templates\ingredient_editor.html:13 msgid "Ingredient Editor" msgstr "" -#: .\cookbook\templates\base.html:267 +#: .\cookbook\templates\base.html:273 #: .\cookbook\templates\export_response.html:7 #: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20 msgid "Export" msgstr "" -#: .\cookbook\templates\base.html:283 .\cookbook\templates\index.html:47 +#: .\cookbook\templates\base.html:285 +msgid "Properties" +msgstr "" + +#: .\cookbook\templates\base.html:299 .\cookbook\views\lists.py:240 +msgid "Unit Conversions" +msgstr "" + +#: .\cookbook\templates\base.html:316 .\cookbook\templates\index.html:47 msgid "Import Recipe" msgstr "" -#: .\cookbook\templates\base.html:285 +#: .\cookbook\templates\base.html:318 msgid "Create" msgstr "" -#: .\cookbook\templates\base.html:298 +#: .\cookbook\templates\base.html:331 #: .\cookbook\templates\generic\list_template.html:14 msgid "External Recipes" msgstr "" -#: .\cookbook\templates\base.html:301 .\cookbook\templates\space_manage.html:15 +#: .\cookbook\templates\base.html:334 .\cookbook\templates\space_manage.html:15 msgid "Space Settings" msgstr "" -#: .\cookbook\templates\base.html:306 .\cookbook\templates\system.html:13 +#: .\cookbook\templates\base.html:339 .\cookbook\templates\system.html:13 msgid "System" msgstr "" -#: .\cookbook\templates\base.html:308 +#: .\cookbook\templates\base.html:341 msgid "Admin" msgstr "" -#: .\cookbook\templates\base.html:312 +#: .\cookbook\templates\base.html:345 #: .\cookbook\templates\space_overview.html:25 msgid "Your Spaces" msgstr "" -#: .\cookbook\templates\base.html:323 +#: .\cookbook\templates\base.html:356 #: .\cookbook\templates\space_overview.html:6 msgid "Overview" msgstr "" -#: .\cookbook\templates\base.html:327 +#: .\cookbook\templates\base.html:366 msgid "Markdown Guide" msgstr "" -#: .\cookbook\templates\base.html:329 +#: .\cookbook\templates\base.html:368 msgid "GitHub" msgstr "" -#: .\cookbook\templates\base.html:331 +#: .\cookbook\templates\base.html:370 msgid "Translate Tandoor" msgstr "" -#: .\cookbook\templates\base.html:335 +#: .\cookbook\templates\base.html:374 msgid "API Browser" msgstr "" -#: .\cookbook\templates\base.html:338 +#: .\cookbook\templates\base.html:377 msgid "Log out" msgstr "" -#: .\cookbook\templates\base.html:360 +#: .\cookbook\templates\base.html:400 msgid "You are using the free version of Tandor" msgstr "" -#: .\cookbook\templates\base.html:361 +#: .\cookbook\templates\base.html:401 msgid "Upgrade Now" msgstr "" @@ -1501,11 +1602,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "" @@ -1886,11 +1987,7 @@ msgstr "" msgid "Start your own recipe space and invite other users to it." msgstr "" -#: .\cookbook\templates\system.html:20 -msgid "System Information" -msgstr "" - -#: .\cookbook\templates\system.html:22 +#: .\cookbook\templates\system.html:14 msgid "" "\n" " Django Recipes is an open source free software application. It can " @@ -1901,21 +1998,33 @@ msgid "" " " msgstr "" -#: .\cookbook\templates\system.html:36 +#: .\cookbook\templates\system.html:20 +msgid "System Information" +msgstr "" + +#: .\cookbook\templates\system.html:41 +msgid "" +"\n" +" You need to execute version.py in your update " +"script to generate version information (done automatically in docker).\n" +" " +msgstr "" + +#: .\cookbook\templates\system.html:46 msgid "Media Serving" msgstr "" -#: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52 -#: .\cookbook\templates\system.html:68 +#: .\cookbook\templates\system.html:47 .\cookbook\templates\system.html:61 +#: .\cookbook\templates\system.html:75 msgid "Warning" msgstr "" -#: .\cookbook\templates\system.html:37 .\cookbook\templates\system.html:52 -#: .\cookbook\templates\system.html:68 .\cookbook\templates\system.html:83 +#: .\cookbook\templates\system.html:47 .\cookbook\templates\system.html:61 +#: .\cookbook\templates\system.html:75 .\cookbook\templates\system.html:88 msgid "Ok" msgstr "" -#: .\cookbook\templates\system.html:39 +#: .\cookbook\templates\system.html:49 msgid "" "Serving media files directly using gunicorn/python is not recommend!\n" " Please follow the steps described\n" @@ -1925,16 +2034,16 @@ msgid "" " " msgstr "" -#: .\cookbook\templates\system.html:45 .\cookbook\templates\system.html:61 -#: .\cookbook\templates\system.html:76 .\cookbook\templates\system.html:90 +#: .\cookbook\templates\system.html:55 .\cookbook\templates\system.html:70 +#: .\cookbook\templates\system.html:83 .\cookbook\templates\system.html:95 msgid "Everything is fine!" msgstr "" -#: .\cookbook\templates\system.html:50 +#: .\cookbook\templates\system.html:59 msgid "Secret Key" msgstr "" -#: .\cookbook\templates\system.html:54 +#: .\cookbook\templates\system.html:63 msgid "" "\n" " You do not have a SECRET_KEY configured in your " @@ -1947,11 +2056,11 @@ msgid "" " " msgstr "" -#: .\cookbook\templates\system.html:66 +#: .\cookbook\templates\system.html:73 msgid "Debug Mode" msgstr "" -#: .\cookbook\templates\system.html:70 +#: .\cookbook\templates\system.html:77 msgid "" "\n" " This application is still running in debug mode. This is most " @@ -1962,15 +2071,15 @@ msgid "" " " msgstr "" -#: .\cookbook\templates\system.html:81 +#: .\cookbook\templates\system.html:86 msgid "Database" msgstr "" -#: .\cookbook\templates\system.html:83 +#: .\cookbook\templates\system.html:88 msgid "Info" msgstr "" -#: .\cookbook\templates\system.html:85 +#: .\cookbook\templates\system.html:90 msgid "" "\n" " This application is not running with a Postgres database " @@ -1983,258 +2092,258 @@ msgstr "" msgid "URL Import" msgstr "" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:120 .\cookbook\views\api.py:214 msgid "Parameter updated_at incorrectly formatted" msgstr "" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:234 .\cookbook\views\api.py:341 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with the same object!" msgstr "" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:245 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:250 msgid "Cannot merge with child object!" msgstr "" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:286 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:292 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:350 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:353 .\cookbook\views\api.py:371 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:356 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:362 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:368 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:586 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:591 .\cookbook\views\api.py:1006 +#: .\cookbook\views\api.py:1019 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:778 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:780 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:824 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:826 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:829 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:832 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:835 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:838 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:840 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:843 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:845 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:847 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:849 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:850 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:852 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:853 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:855 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:857 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:859 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:861 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:863 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:865 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:867 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:869 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:871 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:873 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:875 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:877 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:879 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:1099 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:1104 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:1107 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1343 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1384 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1391 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1396 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1423 msgid "No usable data could be found." msgstr "" -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1516 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1595 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1617 msgid "Sync successful!" msgstr "" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1622 msgid "Error synchronizing with Storage" msgstr "" @@ -2295,7 +2404,7 @@ msgstr "" msgid "Error saving changes!" msgstr "" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2333,6 +2442,10 @@ msgstr "" msgid "Steps" msgstr "" +#: .\cookbook\views\lists.py:255 +msgid "Property Types" +msgstr "" + #: .\cookbook\views\new.py:121 msgid "Imported new recipe!" msgstr "" @@ -2341,73 +2454,73 @@ msgstr "" msgid "There was an error importing this recipe!" msgstr "" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:69 .\cookbook\views\views.py:187 +#: .\cookbook\views\views.py:209 .\cookbook\views\views.py:395 +msgid "This feature is not available in the demo version!" +msgstr "" + +#: .\cookbook\views\views.py:85 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:139 msgid "You do not have the required permissions to perform this action!" msgstr "" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:150 msgid "Comment saved!" msgstr "" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:249 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:254 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:258 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:334 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " "on how to reset passwords." msgstr "" -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:341 msgid "Passwords dont match!" msgstr "" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:349 msgid "User has been created, please login!" msgstr "" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:365 msgid "Malformed Invite Link supplied!" msgstr "" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:382 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:388 msgid "Invite Link not valid or already used!" msgstr "" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:405 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:411 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/es/LC_MESSAGES/django.mo b/cookbook/locale/es/LC_MESSAGES/django.mo index 86d8724c9..a5a17f3cd 100644 Binary files a/cookbook/locale/es/LC_MESSAGES/django.mo and b/cookbook/locale/es/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/es/LC_MESSAGES/django.po b/cookbook/locale/es/LC_MESSAGES/django.po index 820a2a340..14802d860 100644 --- a/cookbook/locale/es/LC_MESSAGES/django.po +++ b/cookbook/locale/es/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2023-03-13 06:55+0000\n" -"Last-Translator: Amara Ude \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-09-25 09:59+0000\n" +"Last-Translator: Matias Laporte \n" "Language-Team: Spanish \n" "Language: es\n" @@ -65,7 +65,7 @@ msgstr "Número de decimales del ingrediente" msgid "Shopping list auto sync period" msgstr "Período de sincronización automática de la lista de compras" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Comentarios" @@ -117,7 +117,7 @@ msgstr "Número de decimales para redondear los ingredientes." msgid "If you want to be able to create and see comments underneath recipes." msgstr "Si desea poder crear y ver comentarios debajo de las recetas." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -135,7 +135,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Hace la barra de navegación fija en la parte superior de la página." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" "Añadir de manera automática los ingredientes del plan a la lista de la " @@ -157,11 +157,11 @@ msgstr "" "Ambos campos son opcionales. Si no se proporciona ninguno, se mostrará el " "nombre de usuario en su lugar" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Nombre" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Palabras clave" @@ -173,7 +173,7 @@ msgstr "Tiempo de preparación en minutos" msgid "Waiting time (cooking/baking) in minutes" msgstr "Tiempo de espera (cocinar/hornear) en minutos" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Ruta" @@ -181,11 +181,11 @@ msgstr "Ruta" msgid "Storage UID" msgstr "UID de almacenamiento" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Por defecto" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -193,22 +193,22 @@ msgstr "" "Para evitar duplicados, las recetas con el mismo nombre serán ignoradas. " "Marca esta opción para importar todas las recetas." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Añada su comentario: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" "Déjelo vacío para Dropbox e ingrese la contraseña de la aplicación para " "nextcloud." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" "Déjelo en blanco para nextcloud e ingrese el token de api para dropbox." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -216,33 +216,33 @@ msgstr "" "Dejar vació para Dropbox e introducir sólo la URL base para Nextcloud " "(/remote.php/webdav/ se añade automáticamente)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Almacenamiento" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Activo" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Cadena de búsqueda" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "ID de Fichero" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Debe proporcionar al menos una receta o un título." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Puede enumerar los usuarios predeterminados con los que compartir recetas en " "la configuración." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -250,15 +250,15 @@ msgstr "" "Puede utilizar Markdown para formatear este campo. Vea la documentación aqui" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Se ha alcanzado el número máximo de usuarios en este espacio." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "¡El correo electrónico ya existe!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -266,15 +266,15 @@ msgstr "" "El correo electrónico es opcional. Si se añade uno se mandará un link de " "invitación." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "El nombre ya existe." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Aceptar términos y condiciones" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -283,7 +283,7 @@ msgstr "" "similitud de trigramas(Ej. Valores más pequeños indican que más fallos se " "van a ignorar)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -291,7 +291,7 @@ msgstr "" "Selecciona el tipo de búsqueda. Haz click aquí para una descripción completa de las opciones." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -299,7 +299,7 @@ msgstr "" "Utilizar comparación difusa en unidades, palabras clave e ingredientes al " "editar e importar recetas." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -307,7 +307,7 @@ msgstr "" "Campos de búsqueda ignorando acentos.  La selección de esta opción puede " "mejorar o degradar la calidad de la búsqueda dependiendo del idioma" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" @@ -315,7 +315,7 @@ msgstr "" "Campos de búsqueda para coincidencias parciales. (por ejemplo, buscar 'Pie' " "devolverá 'pie' y 'piece' y 'soapie')" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -323,7 +323,7 @@ msgstr "" "Campos de búsqueda para coincidencias al principio de la palabra. (por " "ejemplo, buscar 'sa' devolverá 'ensalada' y 'sándwich')" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -332,7 +332,7 @@ msgstr "" "'receta'). Nota: esta opción entrará en conflicto con los métodos de " "búsqueda 'web' y 'raw'." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -340,35 +340,35 @@ msgstr "" "Campos para búsqueda de texto completo. Nota: los métodos de búsqueda 'web', " "'phrase' y 'raw' solo funcionan con campos de texto completo." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Método de Búsqueda" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Búsquedas difusas" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Ignorar Acento" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Coincidencia Parcial" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Comienza Con" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Búsqueda Difusa" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Texto Completo" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -376,7 +376,7 @@ msgstr "" "Los usuarios verán todos los elementos que agregues a tu lista de compras. " "Deben agregarte para ver los elementos en su lista." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -384,7 +384,7 @@ msgstr "" "Al agregar un plan de comidas a la lista de compras (manualmente o " "automáticamente), incluir todas las recetas relacionadas." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -392,96 +392,96 @@ msgstr "" "Al agregar un plan de comidas a la lista de compras (manualmente o " "automáticamente), excluir los ingredientes que están disponibles." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" "Número predeterminado de horas para retrasar una entrada en la lista de " "compras." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Filtrar la lista de compras para incluir solo categorías de supermercados." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "Días de entradas recientes en la lista de compras a mostrar." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Marcar los alimentos como 'Disponible' cuando se marca en la lista de " "compras." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Delimitador a utilizar para exportaciones CSV." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "Prefijo a agregar al copiar la lista al portapapeles." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Compartir Lista de la Compra" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Autosincronización" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Agregar Plan de Comidas automáticamente" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Excluir Disponible" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Incluir Relacionados" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Horas de Retraso Predeterminadas" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Filtrar según Supermercado" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Días Recientes" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "Delimitador CSV" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Prefijo de la lista" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Auto en existencia" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Restablecer la herencia de alimentos" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Reiniciar todos los alimentos para heredar los campos configurados." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "Campos en los alimentos que deben ser heredados por defecto." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Mostrar cantidad de recetas en los filtros de búsquedas" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" "Utilice la forma plural para las unidades y alimentos dentro de este espacio." @@ -495,7 +495,7 @@ msgstr "" "favor, espere unos minutos e inténtelo de nuevo." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "¡No ha iniciado sesión y por lo tanto no puede ver esta página!" @@ -508,7 +508,7 @@ msgstr "¡No ha iniciado sesión y por lo tanto no puede ver esta página!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "¡No tienes los permisos necesarios para ver esta página!" @@ -527,11 +527,41 @@ msgstr "Ha alcanzado el número máximo de recetas para su espacio." msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "Use fractions" +msgid "reverse rotation" +msgstr "Usar fracciones" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "amasar" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "espesar" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "precalentar" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "fermentar" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Debe proporcionar un tamaño de porción" @@ -543,13 +573,13 @@ msgstr "" #: .\cookbook\integration\copymethat.py:44 #: .\cookbook\integration\melarecipes.py:37 msgid "Favorite" -msgstr "" +msgstr "Favorito" #: .\cookbook\integration\copymethat.py:50 msgid "I made this" -msgstr "" +msgstr "Lo he preparado" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -557,30 +587,34 @@ msgstr "" "El importador esperaba un fichero.zip. ¿Has escogido el tipo de importador " "correcto para tus datos?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "Las siguentes recetas han sido ignordas por que ya existen:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "Se importaron %s recetas." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" +msgstr "Fuente de la receta:" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Notas" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Información Nutricional" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Fuente" @@ -611,19 +645,21 @@ msgstr "Sección" #: .\cookbook\management\commands\rebuildindex.py:14 msgid "Rebuilds full text search index on Recipe" -msgstr "" +msgstr "Reconstruye el índice de búsqueda por texto completo de la receta" #: .\cookbook\management\commands\rebuildindex.py:18 msgid "Only Postgresql databases use full text search, no index to rebuild" msgstr "" +"Solo las bases de datos Postgresql utilizan la búsqueda por texto completo, " +"no hay índice para reconstruir" #: .\cookbook\management\commands\rebuildindex.py:29 msgid "Recipe index rebuild complete." -msgstr "" +msgstr "Se reconstruyó el índice de la receta." #: .\cookbook\management\commands\rebuildindex.py:31 msgid "Recipe index rebuild failed." -msgstr "" +msgstr "No fue posible reconstruir el índice de la receta." #: .\cookbook\migrations\0047_auto_20200602_1133.py:14 msgid "Breakfast" @@ -647,155 +683,159 @@ msgid "" "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Buscar" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Régimen de comidas" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Libros" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" -msgstr "" +msgstr " es parte del paso de una receta y no puede ser eliminado" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" -msgstr "" +msgstr "Simple" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" -msgstr "" +msgstr "Frase" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" -msgstr "" +msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" -msgstr "" +msgstr "Crudo" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Alias de la Comida" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Units" msgid "Unit Alias" msgstr "Unidades" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Keywords" msgid "Keyword Alias" msgstr "Palabras clave" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Description" msgid "Description Replace" msgstr "Descripción" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Instructions" msgid "Instruction Replace" msgstr "Instrucciones" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Receta" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 #, fuzzy #| msgid "Food" msgid "Food" msgstr "Comida" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Palabra clave" #: .\cookbook\serializer.py:198 msgid "File uploads are not enabled for this Space." -msgstr "" +msgstr "Las cargas de archivo no están habilitadas para esta Instancia." #: .\cookbook\serializer.py:209 msgid "You have reached your file upload limit." -msgstr "" +msgstr "Has alcanzado el límite de cargas de archivo." #: .\cookbook\serializer.py:291 msgid "Cannot modify Space owner permission." -msgstr "" +msgstr "No puedes modificar los permisos del propietario de la Instancia." -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" -msgstr "" +msgstr "Hola" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " -msgstr "" +msgstr "Has sido invitado por: " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " -msgstr "" +msgstr " para unirte a su instancia de Tandoor Recipes " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " -msgstr "" +msgstr "Haz click en el siguiente enlace para activar tu cuenta: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" +"Si el enlace no funciona, utiliza el siguiente código para unirte " +"manualmente a la instancia: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " -msgstr "" +msgstr "La invitación es válida hasta " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" +"Tandoor Recipes es un administrador de recetas Open Source. Dale una ojeada " +"en GitHub " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" -msgstr "" +msgstr "Invitación para Tandoor Recipes" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1664,11 +1704,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "por" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Comentario" @@ -2207,266 +2247,266 @@ msgstr "" msgid "URL Import" msgstr "Importar URL" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 #, fuzzy #| msgid "Parameter filter_list incorrectly formatted" msgid "Parameter updated_at incorrectly formatted" msgstr "Parámetro filter_list formateado incorrectamente" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "¡No se puede unir con el mismo objeto!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 #, fuzzy #| msgid "Cannot merge with the same object!" msgid "Cannot merge with child object!" msgstr "¡No se puede unir con el mismo objeto!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 #, fuzzy #| msgid "The requested page could not be found." msgid "No usable data could be found." msgstr "La página solicitada no pudo ser encontrada." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "La importación no está implementada para este proveedor" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 #, fuzzy #| msgid "This feature is not available in the demo version!" msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "¡Esta funcionalidad no está disponible en la versión demo!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "¡Sincronización exitosa!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Error de sincronización con el almacenamiento" @@ -2529,7 +2569,7 @@ msgstr "¡Cambios guardados!" msgid "Error saving changes!" msgstr "¡Error al guardar los cambios!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2581,40 +2621,40 @@ msgstr "¡Nueva receta importada!" msgid "There was an error importing this recipe!" msgstr "¡Hubo un error al importar esta receta!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "¡Esta funcionalidad no está disponible en la versión demo!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "¡No tienes los permisos necesarios para realizar esta acción!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "¡Comentario guardado!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "¡Esta funcionalidad no está disponible en la versión demo!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2624,33 +2664,33 @@ msgstr "" "usuario. Si has olvidado tus credenciales de superusuario, por favor " "consulta la documentación de django sobre cómo restablecer las contraseñas." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "¡Las contraseñas no coinciden!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "El usuario ha sido creado, ¡inicie sesión!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "¡Se proporcionó un enlace de invitación con formato incorrecto!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "¡El enlace de invitación no es válido o ya se ha utilizado!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/fr/LC_MESSAGES/django.mo b/cookbook/locale/fr/LC_MESSAGES/django.mo index 6c014f9b2..ff2ecd186 100644 Binary files a/cookbook/locale/fr/LC_MESSAGES/django.mo and b/cookbook/locale/fr/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/fr/LC_MESSAGES/django.po b/cookbook/locale/fr/LC_MESSAGES/django.po index 83db28c48..7c9dae10c 100644 --- a/cookbook/locale/fr/LC_MESSAGES/django.po +++ b/cookbook/locale/fr/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2023-03-13 06:55+0000\n" -"Last-Translator: Jin Zhang \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-09-02 20:25+0000\n" +"Last-Translator: AJ \n" "Language-Team: French \n" "Language: fr\n" @@ -65,7 +65,7 @@ msgstr "Nombre de décimales pour les ingrédients" msgid "Shopping list auto sync period" msgstr "Période de synchro automatique de la liste de courses" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Commentaires" @@ -121,7 +121,7 @@ msgstr "" "Si vous souhaitez pouvoir créer et consulter des commentaires en dessous des " "recettes." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -139,7 +139,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Épingler la barre de navigation en haut de la page." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" "Ajouter les ingrédients du menu de la semaine à la liste de courses " @@ -161,11 +161,11 @@ msgstr "" "Les deux champs sont facultatifs. Si aucun n’est rempli, le nom " "d’utilisateur sera affiché à la place" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Nom" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Mots-clés" @@ -177,7 +177,7 @@ msgstr "Temps de préparation en minutes" msgid "Waiting time (cooking/baking) in minutes" msgstr "Temps d’attente (cuisson) en minutes" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Chemin" @@ -185,11 +185,11 @@ msgstr "Chemin" msgid "Storage UID" msgstr "UID de stockage" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Par défaut" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -197,22 +197,22 @@ msgstr "" "Pour éviter les doublons, les recettes de même nom seront ignorées. Cocher " "cette case pour tout importer." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Ajoutez votre commentaire : " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" "Laissez vide pour Dropbox et renseignez votre mot de passe d’application " "pour Nextcloud." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" "Laissez vide pour Nextcloud et renseignez votre jeton d’API pour Dropbox." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -220,33 +220,33 @@ msgstr "" "Laisser vide pour Dropbox et saisissez seulement l’URL de base pour " "Nextcloud (/remote.php/webdav/ est ajouté automatiquement)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Stockage" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Actif" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Texte recherché" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "ID du fichier" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Vous devez au moins fournir une recette ou un titre." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Vous pouvez lister les utilisateurs par défaut avec qui partager des " "recettes dans les paramètres." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the
    docs here" @@ -254,15 +254,15 @@ msgstr "" "Vous pouvez utiliser du markdown pour mettre en forme ce champ. Voir la documentation ici" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Nombre maximum d’utilisateurs atteint pour ce groupe." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Adresse mail déjà utilisée !" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -270,15 +270,15 @@ msgstr "" "Une adresse mail n’est pas requise mais si elle est renseignée, le lien " "d’invitation sera envoyé à l’utilisateur." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Nom déjà utilisé." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Accepter les conditions d’utilisation" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -287,15 +287,15 @@ msgstr "" "par similarité de trigrammes (par exemple, des valeurs faibles signifient " "que davantage de fautes de frappe sont ignorées)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" -"Sélectionner la méthode de recherche. Cliquer ici pour une description complète des choix." +"Sélectionner la méthode de recherche. Cliquer ici pour une description complète des choix." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -303,7 +303,7 @@ msgstr "" "Utilisez la correspondance floue sur les unités, les mots-clés et les " "ingrédients lors de l’édition et de l’importation de recettes." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -312,7 +312,7 @@ msgstr "" "peut améliorer ou dégrader la qualité de la recherche en fonction de la " "langue." -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" @@ -320,7 +320,7 @@ msgstr "" "Champs à rechercher pour les correspondances partielles. (par exemple, la " "recherche de « Tarte » renverra « tarte », « tartelette » et « tartes »)" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -329,7 +329,7 @@ msgstr "" "exemple, si vous recherchez « sa », vous obtiendrez « salade » et " "« sandwich»)." -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -338,7 +338,7 @@ msgstr "" "« rectte», vous trouverez « recette ».) Remarque : cette option est " "incompatible avec les méthodes de recherche « web » et « brute »." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -347,35 +347,35 @@ msgstr "" "« web », « phrase » et « brute » ne fonctionnent qu’avec des champs en texte " "intégral." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Méthode de recherche" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Recherches floues" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Ignorer les accents" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "correspondance partielle" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Commence par" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Recherche floue" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Texte intégral" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -384,7 +384,7 @@ msgstr "" "courses. Ils doivent vous ajouter pour que vous puissiez voir les éléments " "de leur liste." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -392,7 +392,7 @@ msgstr "" "Lors de l’ajout d’un menu de la semaine à la liste de courses (manuel ou " "automatique), inclure toutes les recettes connexes." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -400,97 +400,97 @@ msgstr "" "Lors de l’ajout d’un menu de la semaine à la liste de courses (manuel ou " "automatique), exclure les ingrédients disponibles." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" "Nombre d'heures par défaut pour retarder l'ajoût d'un article à la liste de " "courses." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Filtrer la liste de courses pour n’inclure que des catégories de " "supermarchés." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "Jours des entrées récentes de la liste de courses à afficher." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Marquer l’aliment comme disponible lorsqu’il est rayé de la liste de courses." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Caractère de séparation à utiliser pour les exportations CSV." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "Préfixe à ajouter lors de la copie de la liste dans le presse-papiers." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Partager la liste de courses" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Synchronisation automatique" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Ajouter le menu de la semaine automatiquement" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Exclure ingrédients disponibles" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Inclure recettes connexes" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Heures de retard par défaut" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Filtrer par supermarché" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Jours récents" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "Caractère de séparation CSV" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Préfixe de la liste" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Disponible automatique" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Réinitialiser l'héritage alimentaire" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Réinitialiser tous les aliments pour hériter les champs configurés." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "Champs sur les aliments à hériter par défaut." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "" "Afficher le nombre de consultations par recette sur les filtres de recherche" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" "Utiliser la forme plurielle pour les unités et les aliments dans ce groupe." @@ -504,7 +504,7 @@ msgstr "" "Veuillez patienter quelques minutes et réessayer." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "" "Vous n’êtes pas connecté(e) et ne pouvez donc pas afficher cette page !" @@ -518,7 +518,7 @@ msgstr "" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Vous ne disposez pas de droits suffisants pour afficher cette page !" @@ -541,11 +541,39 @@ msgstr "" "Le nombre d’utilisateurs dans votre groupe dépasse le nombre d’utilisateurs " "autorisé." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "Il est nécessaire de fournir soit le queryset, soit la clé de hachage" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +msgid "reverse rotation" +msgstr "sens inverse" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "pétrir" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "réchauffer" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "fermenter" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Vous devez fournir une information de portion" @@ -560,11 +588,10 @@ msgid "Favorite" msgstr "Favori" #: .\cookbook\integration\copymethat.py:50 -#, fuzzy msgid "I made this" msgstr "J'ai fait ça" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -572,7 +599,7 @@ msgstr "" "Un fichier .zip était attendu à l’importation. Avez-vous choisi le bon " "format pour vos données ?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -580,24 +607,28 @@ msgstr "" "Une erreur imprévue est survenue durant l’importation. Vérifiez que vous " "avez téléversé un fichier valide." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "Les recettes suivantes ont été ignorées car elles existaient déjà :" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "%s recettes importées." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" +msgstr "Source de la recette :" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Notes" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Informations nutritionnelles" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Source" @@ -612,7 +643,7 @@ msgstr "Portions" #: .\cookbook\integration\saffron.py:25 msgid "Waiting time" -msgstr "temps d’attente" +msgstr "Temps d’attente" #: .\cookbook\integration\saffron.py:27 msgid "Preparation Time" @@ -668,72 +699,72 @@ msgstr "" "Le stockage maximal de fichiers pour ce groupe en Mo. Mettre 0 pour ne pas " "avoir de limite et -1 pour empêcher le téléversement de fichiers." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Rechercher" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Menu de la semaine" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Livres" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " fait partie d’une étape de la recette et ne peut être supprimé(e)" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Simple" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Phrase" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Internet" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Brut" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Aliment équivalent" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Unité équivalente" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Mot-clé équivalent" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "Remplacer la Description" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "Remplacer l'instruction" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Recette" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "Aliment" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Mot-clé" @@ -749,49 +780,49 @@ msgstr "Vous avez atteint votre limite de téléversement de fichiers." msgid "Cannot modify Space owner permission." msgstr "Impossible de modifier les permissions du propriétaire de groupe." -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Bonjour" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Vous avez été invité par " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " pour rejoindre leur groupe Tandoor Recipes " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Cliquez le lien suivant pour activer votre compte : " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Si le lien ne fonctionne pas, utilisez le code suivant pour rejoindre le " "groupe manuellement : " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "L’invitation est valide jusqu’au " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "Tandoor Recipes est un gestionnaire de recettes open source. Venez-voir " "notre Github " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Invitation Tandoor Recipes" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Liste de courses existante à mettre à jour" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -799,22 +830,22 @@ msgstr "" "Liste d’identifiants d’ingrédient de la recette à ajouter, si non renseigné, " "tous les ingrédients seront ajoutés." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" "Fournir un identifiant de liste de courses et un nombre de portions de 0 " "supprimera cette liste de courses." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "Quantité d’aliments à ajouter à la liste de courses" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "ID de l’unité à utiliser pour la liste de courses" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 #, fuzzy msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1059,8 +1090,8 @@ msgid "" msgstr "" "Le lien de changement du mot de passe n’est pas valide, probablement parce " "qu’il a déjà été utilisé.\n" -" Merci de demander un nouveau changement de mot de passe." +" Merci de demander un nouveau changement de mot de passe." #: .\cookbook\templates\account\password_reset_from_key.html:33 msgid "change password" @@ -1430,7 +1461,7 @@ msgstr "" #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "Rechercher une recette..." +msgstr "Rechercher une recette ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" @@ -1663,11 +1694,11 @@ msgstr "Retour" msgid "Profile" msgstr "Profil" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "par" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Commentaire" @@ -1724,7 +1755,7 @@ msgstr "" " \n" " Les recherches en texte intégral tentent de normaliser les mots " "fournis pour qu'ils correspondent aux variantes courantes. Par exemple : " -"\"forked\", \"forking\", \"forks\" seront tous normalisés en \"fork\".\n" +"'forked', 'forking', 'forks' seront tous normalisés en 'fork'.\n" " Il existe plusieurs méthodes, décrites ci-dessous, qui " "permettent de contrôler la façon dont la recherche doit réagir lorsque " "plusieurs mots sont recherchés.\n" @@ -1983,10 +2014,10 @@ msgstr "" "efficacement. \n" " Vous pouvez reconstruire les index de tous les champs dans la " "page d'administration des recettes, en sélectionnant toutes les recettes et " -"en exécutant la commande \"rebuild index for selected recipes\".\n" +"en exécutant la commande 'rebuild index for selected recipes'.\n" " Vous pouvez également reconstruire les index en ligne de " -"commande en exécutant la commande de gestion \"python manage.py rebuildindex" -"\".\n" +"commande en exécutant la commande de gestion 'python manage.py " +"rebuildindex'.\n" " " #: .\cookbook\templates\settings.html:25 @@ -2368,264 +2399,264 @@ msgstr "" msgid "URL Import" msgstr "Import URL" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "Le paramètre « update_at » n'est pas correctement formaté" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "Il n’existe aucun(e) {self.basename} avec l’identifiant {pk}" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "Impossible de fusionner un objet avec lui-même !" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "Il n’existe aucun(e) {self.basename} avec l’id {target}" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "Impossible de fusionner avec l’objet enfant !" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} a été fusionné avec succès avec {target.name}" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" "Une erreur est survenue lors de la tentative de fusion de {source.name} avec " "{target.name}" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} a été déplacé avec succès vers la racine." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Une erreur est survenue en essayant de déplacer " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "Impossible de déplacer un objet vers lui-même !" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "Il n’existe aucun(e) {self.basename} avec l’id {parent}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} a été déplacé avec succès vers le parent {parent.name}" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} a été supprimé(e) de la liste de courses." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} a été ajouté(e) à la liste de courses." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" "Identifiant de la recette dont fait partie une étape. Pour plusieurs " "paramètres de répétition." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Rien à faire." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "Url non valide" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Connexion refusée." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "Mauvais schéma d’URL." -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "Aucune information utilisable n'a été trouvée." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "L’importation n’est pas implémentée pour ce fournisseur" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" "Cette fonctionnalité n’est pas encore disponible dans la version hébergée de " "Tandoor !" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" -msgstr "Synchro réussie !" +msgstr "Synchronisation réussie !" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Erreur lors de la synchronisation avec le stockage" @@ -2691,7 +2722,7 @@ msgstr "Modifications sauvegardées !" msgid "Error saving changes!" msgstr "Erreur lors de la sauvegarde des modifications !" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2737,7 +2768,12 @@ msgstr "Nouvelle recette importée !" msgid "There was an error importing this recipe!" msgstr "Une erreur est survenue lors de l’importation de cette recette !" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Cette fonctionnalité n’est pas disponible dans la version d’essai !" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2746,25 +2782,20 @@ msgstr "" "Commencez à ajoutez des recettes ou invitez d’autres personnes à vous " "rejoindre." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "Vous n’êtes pas autorisé(e) à effectuer cette action !" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Commentaire sauvegardé !" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Cette fonctionnalité n’est pas disponible dans la version d’essai !" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" "Vous devez sélectionner au moins un champ pour effectuer une recherche !" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2772,12 +2803,12 @@ msgstr "" "Pour utiliser cette méthode de recherche, vous devez sélectionner au moins " "un champ de recherche en texte intégral !" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" "La recherche floue n’est pas compatible avec cette méthode de recherche !" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2788,27 +2819,27 @@ msgstr "" "utilisateur, counsultez la documentation Django pour savoir comment " "réinitialiser le mot de passe." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Les mots de passe ne correspondent pas !" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "L’utilisateur a été créé, veuillez vous connecter !" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "Le lien d’invitation fourni est mal formé !" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Vous avez bien rejoint le groupe." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "Le lien d’invitation est invalide ou déjà utilisé !" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2816,7 +2847,7 @@ msgstr "" "Le signalement de liens partagés n’est pas autorisé pour cette installation. " "Veuillez contacter l’administrateur de la page pour signaler le problème." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/hu_HU/LC_MESSAGES/django.mo b/cookbook/locale/hu_HU/LC_MESSAGES/django.mo index e6b39e7d5..b35f518b5 100644 Binary files a/cookbook/locale/hu_HU/LC_MESSAGES/django.mo and b/cookbook/locale/hu_HU/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/hu_HU/LC_MESSAGES/django.po b/cookbook/locale/hu_HU/LC_MESSAGES/django.po index 63dc1906b..8193bf1a8 100644 --- a/cookbook/locale/hu_HU/LC_MESSAGES/django.po +++ b/cookbook/locale/hu_HU/LC_MESSAGES/django.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2022-05-24 20:32+0000\n" -"Last-Translator: Krisztian Doka \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-04-12 11:55+0000\n" +"Last-Translator: noxonad \n" "Language-Team: Hungarian \n" "Language: hu_HU\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.10.1\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\forms.py:52 msgid "Default unit" @@ -62,7 +62,7 @@ msgstr "Összetevők tizedesjegyei" msgid "Shopping list auto sync period" msgstr "Bevásárlólista automatikus szinkronizálásának periódusa" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Megjegyzések" @@ -116,7 +116,7 @@ msgstr "" "Ha azt szeretné, hogy hozzászólásokat tudjon létrehozni és látni a receptek " "alatt." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -133,7 +133,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "A navigációs sávot az oldal tetejére rögzíti." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" "Automatikusan hozzáadja az étkezési terv hozzávalóit a bevásárlólistához." @@ -154,11 +154,11 @@ msgstr "" "Mindkét mező opcionális. Ha egyiket sem adjuk meg, akkor a felhasználónév " "jelenik meg helyette" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Név" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Kulcsszavak" @@ -170,7 +170,7 @@ msgstr "Előkészítési idő percben" msgid "Waiting time (cooking/baking) in minutes" msgstr "Várakozási idő (sütés/főzés) percben" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Elérési útvonal" @@ -178,11 +178,11 @@ msgstr "Elérési útvonal" msgid "Storage UID" msgstr "Tárhely UID" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Alapértelmezett" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -191,23 +191,23 @@ msgstr "" "recepteket a rendszer figyelmen kívül hagyja. Jelölje be ezt a négyzetet, ha " "mindent importálni szeretne." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Add hozzá a kommented: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" "A dropbox esetében hagyja üresen, a nextcloud esetében pedig adja meg az " "alkalmazás jelszavát." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" "A nextcloud esetében hagyja üresen, a dropbox esetében pedig adja meg az api " "tokent." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -215,33 +215,33 @@ msgstr "" "Hagyja üresen a dropbox esetén, és csak a nextcloud alap url-jét adja meg " "(/remote.php/webdav/ automatikusan hozzáadódik)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Tárhely" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Aktív" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Keresési kifejezés" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "Fájl ID" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Legalább egy receptet vagy címet kell megadnia." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "A beállításokban megadhatja a receptek megosztására szolgáló alapértelmezett " "felhasználókat." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -249,15 +249,15 @@ msgstr "" "A mező formázásához használhatja a markdown formátumot. Lásd a dokumentációt itt" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Elérte a felhasználók maximális számát ezen a területen." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Az e-mail cím már foglalt!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -265,15 +265,15 @@ msgstr "" "Az e-mail cím megadása nem kötelező, de ha van, a meghívó linket elküldi a " "felhasználónak." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "A név már foglalt." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Feltételek és adatvédelem elfogadása" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -282,7 +282,7 @@ msgstr "" "párosítást használ (pl. az alacsony értékek azt jelentik, hogy több gépelési " "hibát figyelmen kívül hagynak)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 #, fuzzy #| msgid "" #| "Select type method of search. Click here " @@ -294,7 +294,7 @@ msgstr "" "Válassza ki a keresés típusát. Kattintson ide " "a lehetőségek teljes leírásáért." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -302,7 +302,7 @@ msgstr "" "A receptek szerkesztése és importálása során az egységek, kulcsszavak és " "összetevők bizonytalan megfeleltetése." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -310,7 +310,7 @@ msgstr "" "Az ékezetek figyelmen kívül hagyásával keresendő mezők. Ennek az opciónak a " "kiválasztása javíthatja vagy ronthatja a keresés minőségét a nyelvtől függően" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" @@ -318,7 +318,7 @@ msgstr "" "Részleges egyezések keresésére szolgáló mezők. (pl. a 'Pie' keresése a " "'pie' és a 'piece' és a 'soapie' kifejezéseket adja vissza.)" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -326,7 +326,7 @@ msgstr "" "Mezők a szó eleji egyezések kereséséhez. (pl. a 'sa' keresés a 'salad' és a " "'sandwich' kifejezéseket adja vissza)" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -335,7 +335,7 @@ msgstr "" "'recipe' szót.) Megjegyzés: ez az opció ütközik a 'web' és a 'raw' keresési " "módszerekkel." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -343,37 +343,37 @@ msgstr "" "Mezők a teljes szöveges kereséshez. Megjegyzés: A 'web', 'phrase' és 'raw' " "keresési módszerek csak teljes szöveges mezőkkel működnek." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Keresési módszer" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Bizonytalan keresések" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Ékezetek ignorálása" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Részleges találat" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 #, fuzzy #| msgid "Starts Wtih" msgid "Starts With" msgstr "Kezdődik a következővel" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Bizonytalan keresés" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Teljes szöveg" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -382,7 +382,7 @@ msgstr "" "Ahhoz, hogy láthassák a saját listájukon szereplő tételeket, hozzá kell " "adniuk téged." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -390,7 +390,7 @@ msgstr "" "Amikor étkezési tervet ad hozzá a bevásárlólistához (kézzel vagy " "automatikusan), vegye fel az összes kapcsolódó receptet." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -398,96 +398,96 @@ msgstr "" "Amikor étkezési tervet ad hozzá a bevásárlólistához (kézzel vagy " "automatikusan), zárja ki a kéznél lévő összetevőket." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "A bevásárlólista bejegyzés késleltetésének alapértelmezett ideje." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Szűrje a bevásárlólistát úgy, hogy csak a szupermarket kategóriákat " "tartalmazza." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "A legutóbbi bevásárlólista bejegyzések megjelenítendő napjai." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Jelölje meg a \" Kéznél van\" jelölést, ha a bevásárlólistáról kipipálta az " "élelmiszert." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "A CSV exportáláshoz használandó elválasztójel." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "A lista vágólapra másolásakor hozzáadandó előtag." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Bevásárlólista megosztása" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Automatikus szinkronizálás" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Automatikus étkezési terv hozzáadása" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Kéznél levő kihagyása" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Tartalmazza a kapcsolódókat" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Alapértelmezett késleltetési órák" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Szűrő a szupermarkethez" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Legutóbbi napok" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "CSV elválasztó" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Lista előtagja" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Automatikus Kéznél lévő" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Élelmiszer-öröklés visszaállítása" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Állítsa vissza az összes ételt, hogy örökölje a konfigurált mezőket." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "" "Az élelmiszerek azon mezői, amelyeket alapértelmezés szerint örökölni kell." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "A receptek számának megjelenítése a keresési szűrőkön" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -500,7 +500,7 @@ msgstr "" "néhány percet, és próbálja meg újra." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Ön nincs bejelentkezve, ezért nem tudja megtekinteni ezt az oldalt!" @@ -513,7 +513,7 @@ msgstr "Ön nincs bejelentkezve, ezért nem tudja megtekinteni ezt az oldalt!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Nem rendelkezik a szükséges jogosultságokkal az oldal megtekintéséhez!" @@ -533,11 +533,41 @@ msgstr "Elérte a maximális számú receptet a helyén." msgid "You have more users than allowed in your space." msgstr "Több felhasználója van, mint amennyit engedélyeztek a térben." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "A queryset vagy a hash_key valamelyikét meg kell adni" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "Use fractions" +msgid "reverse rotation" +msgstr "Törtek használata" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 #, fuzzy #| msgid "You must supply a created_by" msgid "You must supply a servings size" @@ -557,7 +587,7 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -565,7 +595,7 @@ msgstr "" "Az importáló egy .zip fájlt várt. A megfelelő importálótípust választotta az " "adataihoz?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -573,24 +603,30 @@ msgstr "" "Az importálás során váratlan hiba történt. Kérjük, ellenőrizze, hogy " "érvényes fájlt töltött-e fel." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "A következő recepteket figyelmen kívül hagytuk, mert már léteztek:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "Importálva %s recept." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +#, fuzzy +#| msgid "Recipe Home" +msgid "Recipe source:" +msgstr "Recipe Home" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Jegyzetek" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Táplálkozási információk" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Forrás" @@ -665,78 +701,78 @@ msgstr "" "Maximális tárhely a fájloknak MB-ban. 0 a korlátlan, -1 a fájlfeltöltés " "letiltásához." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Keresés" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Étkezési terv" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Könyvek" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " egy recept része, ezért nem törölhető" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Egyszerű" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Kifejezés" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Nyers" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Élelmiszer álneve" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Egység álneve" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Kulcsszó álneve" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Description" msgid "Description Replace" msgstr "Leírás" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Instructions" msgid "Instruction Replace" msgstr "Elkészítés" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Recept" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 #, fuzzy #| msgid "Foods" msgid "Food" msgstr "Ételek" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Kulcsszó" @@ -752,48 +788,48 @@ msgstr "Elérte a fájlfeltöltési limitet." msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Helló" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Önt meghívta " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " hogy csatlakozzon a Tandoor Receptek helyhez " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Kattintson az alábbi linkre fiókja aktiválásához: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Ha a link nem működik, használja a következő kódot, hogy manuálisan " "csatlakozzon a térhez: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "A meghívó a következő időpontig érvényes " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "A Tandoor Receptek egy nyílt forráskódú receptkezelő. Nézze meg a GitHubon " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Tandoor receptek meghívó" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Meglévő bevásárlólista frissítése" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -801,20 +837,20 @@ msgstr "" "A hozzáadandó összetevők azonosítóinak listája a receptből, ha nincs " "megadva, az összes összetevő hozzáadásra kerül." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "A list_recipe azonosító és a 0 adag megadása törli a bevásárlólistát." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "A bevásárlólistához hozzáadandó élelmiszerek mennyisége" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "A bevásárlólistához használandó egység azonosítója" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" "Ha igazra van állítva, akkor minden élelmiszert töröl az aktív " @@ -1435,7 +1471,7 @@ msgstr "" #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "Recept keresése..." +msgstr "Recept keresése ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" @@ -1671,11 +1707,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "által" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Megjegyzés" @@ -2364,83 +2400,83 @@ msgstr "" msgid "URL Import" msgstr "URL importálása" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "Az updated_at paraméter helytelenül van formázva" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "Nem létezik {self.basename} azonosítóval {pk}" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "Nem egyesíthető ugyanazzal az objektummal!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "Nem létezik {self.basename} azonosítóval {target}" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "Nem lehet egyesíteni a gyermekobjektummal!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} sikeresen egyesült a {target.name} -vel" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "Hiba történt a {source.name} és a {target.name} egyesítése során" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} sikeresen átkerült a gyökérbe." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Hiba történt az áthelyezés közben " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "Nem lehet egy objektumot önmagába mozgatni!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "Nem létezik {self.basename} azonosítóval {parent}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} sikeresen átkerült a {parent.name} szülőhöz" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} lekerült a bevásárlólistáról." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} hozzá lett adva a bevásárlólistához." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" "A recept azonosítója, amelynek egy lépés része. Többszörös ismétlés esetén " "paraméter." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "A lekérdezés karakterlánca az objektum nevével összevetve (fuzzy)." -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." @@ -2448,7 +2484,7 @@ msgstr "" "A lekérdezési karakterláncot a recept nevével összevetve (fuzzy). A jövőben " "teljes szöveges keresés is." -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 #, fuzzy #| msgid "ID of keyword a recipe should have. For multiple repeat parameter." msgid "" @@ -2457,132 +2493,132 @@ msgid "" msgstr "" "A recept kulcsszavának azonosítója. Többszörös ismétlődő paraméter esetén." -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" "Az ételek azonosítója egy receptnek tartalmaznia kell. Többszörös ismétlődő " "paraméter esetén." -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "Az egység azonosítója, amellyel a receptnek rendelkeznie kell." -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" "A könyv azonosítója, amelyben a receptnek szerepelnie kell. Többszörös " "ismétlés esetén paraméter." -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "Ha csak a belső recepteket kell visszaadni. [true/false]" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" "Az eredményeket véletlenszerű sorrendben adja vissza. [true/false]" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" "Az új találatokat adja vissza először a keresési eredmények között. [true/" "false]" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 #, fuzzy #| msgid "If only internal recipes should be returned. [true/false]" msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "Ha csak a belső recepteket kell visszaadni. [true/false]" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." @@ -2590,7 +2626,7 @@ msgstr "" "Visszaadja az id elsődleges kulccsal rendelkező bevásárlólista-bejegyzést. " "Több érték megengedett." -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." @@ -2599,48 +2635,48 @@ msgstr "" "mindkettő, legutóbbi]
    – a legutóbbi a nem bejelölt és a nemrég " "befejezett elemeket tartalmazza." -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" "Visszaadja a bevásárlólista bejegyzéseit szupermarket kategóriák szerinti " "sorrendben." -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Semmi feladat." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Kapcsolat megtagadva." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 #, fuzzy #| msgid "No useable data could be found." msgid "No usable data could be found." msgstr "Nem találtam használható adatokat." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "Az importálás nincs implementálva ennél a szolgáltatónál" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "Ez a funkció még nem érhető el a tandoor hosztolt verziójában!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Szinkronizálás sikeres!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Hiba szinkronizálás közben a tárolóval" @@ -2705,7 +2741,7 @@ msgstr "Változások mentve!" msgid "Error saving changes!" msgstr "Hiba a módosítások mentése közben!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2755,7 +2791,12 @@ msgstr "Új recept importálva!" msgid "There was an error importing this recipe!" msgstr "Hiba történt a recept importálásakor!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Ez a funkció nem érhető el a demó verzióban!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2763,24 +2804,19 @@ msgstr "" "Sikeresen létrehozta saját receptterét. Kezdje el néhány recept " "hozzáadásával, vagy hívjon meg másokat is, hogy csatlakozzanak Önhöz." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "Nem rendelkezik a művelet végrehajtásához szükséges jogosultságokkal!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Megjegyzés mentve!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Ez a funkció nem érhető el a demó verzióban!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "Legalább egy mezőt ki kell választania a kereséshez!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2788,11 +2824,11 @@ msgstr "" "Ennek a keresési módszernek a használatához legalább egy teljes szöveges " "keresési mezőt ki kell választania!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "A bizonytalan keresés nem kompatibilis ezzel a keresési módszerrel!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2802,27 +2838,27 @@ msgstr "" "elfelejtette a szuperfelhasználói hitelesítő adatait, kérjük, olvassa el a " "django dokumentációját a jelszavak visszaállításáról." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "A jelszavak nem egyeznek!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "A felhasználó létre lett hozva, kérjük, jelentkezzen be!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "Hibás meghívó linket küldtek!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Sikeresen csatlakozott az térhez." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "A meghívó link nem érvényes vagy már felhasználásra került!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2830,7 +2866,7 @@ msgstr "" "A megosztási hivatkozások jelentése nem engedélyezett ezen a példányon. " "Kérjük, a problémák jelentéséhez értesítse az oldal adminisztrátorát." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/id/LC_MESSAGES/django.mo b/cookbook/locale/id/LC_MESSAGES/django.mo index 8e0589858..a34a4b024 100644 Binary files a/cookbook/locale/id/LC_MESSAGES/django.mo and b/cookbook/locale/id/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/it/LC_MESSAGES/django.mo b/cookbook/locale/it/LC_MESSAGES/django.mo index 1168fa818..babc1b13d 100644 Binary files a/cookbook/locale/it/LC_MESSAGES/django.mo and b/cookbook/locale/it/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/it/LC_MESSAGES/django.po b/cookbook/locale/it/LC_MESSAGES/django.po index 21eec4eb9..89bd5df86 100644 --- a/cookbook/locale/it/LC_MESSAGES/django.po +++ b/cookbook/locale/it/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2023-02-05 03:55+0000\n" -"Last-Translator: Adri \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-04-29 07:55+0000\n" +"Last-Translator: Oliver Cervera \n" "Language-Team: Italian \n" "Language: it\n" @@ -63,7 +63,7 @@ msgstr "Posizioni decimali degli ingredienti" msgid "Shopping list auto sync period" msgstr "Frequenza di sincronizzazione automatica della lista della spesa" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Commenti" @@ -116,7 +116,7 @@ msgid "If you want to be able to create and see comments underneath recipes." msgstr "" "Se vuoi essere in grado di creare e vedere i commenti sotto le ricette." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -134,7 +134,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Fissa la barra di navigazione nella parte superiore della pagina." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" "Aggiungi automaticamente gli ingredienti del piano alimentare alla lista " @@ -156,11 +156,11 @@ msgstr "" "Entrambi i campi sono facoltativi. Se non viene fornito, verrà visualizzato " "il nome utente" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Nome" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Parole chiave" @@ -172,7 +172,7 @@ msgstr "Tempo di preparazione in minuti" msgid "Waiting time (cooking/baking) in minutes" msgstr "Tempo di attesa (cottura) in minuti" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Percorso" @@ -180,11 +180,11 @@ msgstr "Percorso" msgid "Storage UID" msgstr "UID di archiviazione" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Predefinito" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -192,20 +192,20 @@ msgstr "" "Per prevenire duplicati, vengono ignorate le ricette che hanno lo stesso " "nome di quelle esistenti. Metti la spunta per importare tutto." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Aggiungi il tuo commento: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" "Lascia vuoto per dropbox e inserisci la password dell'app per nextcloud." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "Lascia vuoto per nextcloud e inserisci l'api token per dropbox." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -213,33 +213,33 @@ msgstr "" "Lascia vuoto per dropbox e inserisci solo l'url base per nextcloud (/" "remote.php/webdav/ è aggiunto automaticamente)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Archiviazione" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Attivo" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Stringa di Ricerca" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "ID del File" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Devi fornire almeno una ricetta o un titolo." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "È possibile visualizzare l'elenco degli utenti predefiniti con cui " "condividere le ricette nelle impostazioni." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -247,15 +247,15 @@ msgstr "" "Puoi usare markdown per formattare questo campo. Guarda la documentazione qui" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "È stato raggiunto il numero massimo di utenti per questa istanza." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Questo indirizzo email è già in uso!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -263,15 +263,15 @@ msgstr "" "Non è obbligatorio specificare l'indirizzo email, ma se presente verrà " "utilizzato per mandare all'utente un link di invito." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Nome già in uso." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Accetta i Termini d'uso e Privacy" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -280,7 +280,7 @@ msgstr "" "trigrammi (ad esempio, valori bassi significano che vengono ignorati più " "errori di battitura)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -288,7 +288,7 @@ msgstr "" "Seleziona il metodo di ricerca. Clicca qui " "per avere maggiori informazioni." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -296,7 +296,7 @@ msgstr "" "Usa la corrispondenza vaga per unità, parole chiave e ingredienti durante la " "modifica e l'importazione di ricette." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -304,15 +304,15 @@ msgstr "" "Campi da cercare ignorando gli accenti. A seconda alla lingua utilizzata, " "questa opzione può migliorare o peggiorare la ricerca" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -"Campi da cercare con corrispondenza parziale. (ad esempio, cercando \"Torta" -"\" verranno mostrati \"torta\", \"tortino\" e \"contorta\")" +"Campi da cercare con corrispondenza parziale. (ad esempio, cercando 'Torta' " +"verranno mostrati 'torta', 'tortino' e 'contorta')" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -320,16 +320,16 @@ msgstr "" "Campi da cercare all'inizio di parole corrispondenti (es. cercando per 'ins' " "mostrerà 'insalata' e 'insaccati')" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" "Campi in cui usare la ricerca 'vaga'. (ad esempio cercando per 'riceta' " -"verrà mostrato 'ricetta'). Nota: questa opzione non è compatibile con la " +"verrà mostrato 'ricetta'). Nota: questa opzione non è compatibile con la " "ricerca 'web' o 'raw'." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -337,35 +337,35 @@ msgstr "" "Campi per la ricerca full-text. Nota: i metodi di ricerca 'web', 'frase' e " "'raw' funzionano solo con i campi full-text." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Metodo di ricerca" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "Ricerche vaghe" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Ignora accento" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Corrispondenza parziale" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Inizia con" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "Ricerca vaga" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Full Text" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -373,7 +373,7 @@ msgstr "" "Gli utenti potranno vedere tutti gli elementi che aggiungi alla tua lista " "della spesa. Devono aggiungerti per vedere gli elementi nella loro lista." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -381,7 +381,7 @@ msgstr "" "Quando si aggiunge un piano alimentare alla lista della spesa (manualmente o " "automaticamente), includi tutte le ricette correlate." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -389,97 +389,97 @@ msgstr "" "Quando si aggiunge un piano alimentare alla lista della spesa (manualmente o " "automaticamente), escludi gli ingredienti già disponibili." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" "Il numero predefinito di ore per ritardare l'inserimento di una lista della " "spesa." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" "Filtra la lista della spesa per includere solo categorie dei supermercati." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "Giorni di visualizzazione di voci recenti della lista della spesa." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Contrassegna gli alimenti come 'Disponibili' quando spuntati dalla lista " "della spesa." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Delimitatore usato per le esportazioni CSV." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "Prefisso da aggiungere quando si copia una lista negli appunti." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Condividi lista della spesa" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Sincronizzazione automatica" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Aggiungi automaticamente al piano alimentare" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Escludi Disponibile" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Includi correlati" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Ore di ritardo predefinite" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Filtra per supermercato" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Giorni recenti" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "Delimitatore CSV" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Prefisso lista" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Disponibilità automatica" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Ripristina Eredità Alimenti" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Ripristina tutti gli alimenti per ereditare i campi configurati." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "" "Campi su alimenti che devono essere ereditati per impostazione predefinita." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Mostra il conteggio delle ricette nei filtri di ricerca" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" "Usare la forma plurale per le unità e gli alimenti all'interno di questo " @@ -494,7 +494,7 @@ msgstr "" "riprova." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Non sei loggato e quindi non puoi visualizzare questa pagina!" @@ -507,7 +507,7 @@ msgstr "Non sei loggato e quindi non puoi visualizzare questa pagina!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Non hai i permessi necessari per visualizzare questa pagina!" @@ -526,11 +526,39 @@ msgstr "Hai raggiunto il numero massimo di ricette nella tua istanza." msgid "You have more users than allowed in your space." msgstr "Hai più utenti di quanti permessi nella tua istanza." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "Uno tra queryset o has_key deve essere fornito" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +msgid "reverse rotation" +msgstr "rotazione inversa" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Devi fornire le dimensione delle porzioni" @@ -548,7 +576,7 @@ msgstr "Preferito" msgid "I made this" msgstr "L'ho preparato" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" @@ -556,7 +584,7 @@ msgstr "" "La procedura di import necessita di un file .zip. Hai scelto il tipo di " "importazione corretta per i tuoi dati?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -564,24 +592,28 @@ msgstr "" "Un errore imprevisto si è verificato durante l'importazione. Assicurati di " "aver caricato un file valido." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "Le seguenti ricette sono state ignorate perché già esistenti:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "Importate %s ricette." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" +msgstr "Fonte ricetta:" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Note" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Informazioni nutrizionali" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Fonte" @@ -652,72 +684,72 @@ msgstr "" "Archiviazione massima in MB. 0 per illimitata, -1 per disabilitare il " "caricamento dei file." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Cerca" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Piano alimentare" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Libri" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " è parte dello step di una ricetta e non può essere eliminato" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Semplice" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Frase" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Raw" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Alias Alimento" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Alias Unità" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Alias Parola Chiave" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "Sostituisci Descrizione" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "Sostituisci Istruzione" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Ricetta" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "Alimento" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Parola chiave" @@ -733,49 +765,49 @@ msgstr "Hai raggiungo il limite per il caricamento dei file." msgid "Cannot modify Space owner permission." msgstr "Impossibile modificare i permessi del proprietario dell'istanza." -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Ciao" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Sei stato invitato da " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " per entrare nella sua istanza di Tandoor Recipes " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Clicca il link qui di seguito per attivare il tuo account: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Se il link non funziona, usa il seguente codice per entrare manualmente " "nell'istanza: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "L'invito è valido fino al " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "Tandoor Recipes è un gestore di ricette Open Source. Dagli una occhiata su " "GitHub " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Invito per Tandoor Recipes" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Lista della spesa esistente da aggiornare" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -783,22 +815,22 @@ msgstr "" "Lista degli ID degli ingredienti dalla ricetta da aggiungere, se non è " "fornita saranno aggiunti tutti gli ingredienti." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" "Fornendo un ID list_recipe e impostando le porzioni a 0, la lista della " "spesa verrà eliminata." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "Quantità di alimenti da aggiungere alla lista della spesa" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "ID dell'unità da usare per la lista della spesa" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" "Quando impostato su vero, eliminerà tutti gli alimenti dalle liste della " @@ -1042,7 +1074,7 @@ msgid "" msgstr "" "Il link per il reset della password non è corretto, probabilmente perché è " "stato già utilizzato.\n" -" Puoi richiedere un nuovo reset della password." #: .\cookbook\templates\account\password_reset_from_key.html:33 @@ -1648,11 +1680,11 @@ msgstr "Indietro" msgid "Profile" msgstr "Profilo" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "di" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Commento" @@ -1707,15 +1739,15 @@ msgid "" msgstr "" " \n" " Le ricerche full-text cercano di normalizzare le parole fornite " -"per abbinare varianti comuni. Ad esempio, \"separato\", \"separando\", " -"\"separa\" verranno tutti normalizzati in \"separare\".\n" +"per abbinare varianti comuni. Ad esempio, 'separato', 'separando', 'separa' " +"verranno tutti normalizzati in 'separare'.\n" " Ci sono diversi metodi disponibili, descritti di seguito, che " "controlleranno il comportamento della ricerca in caso di ricerca con più " "parole.\n" " I dettagli tecnici completi su come questi funzionano possono " "essere visualizzati sul sito web di " -"Postgresql.\n" +"textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES>sito web di Postgresql.\n" " " #: .\cookbook\templates\search_info.html:29 @@ -1893,10 +1925,12 @@ msgid "" "Allows fine control over search results but might not return results if too " "many spelling mistakes are made." msgstr "" +"Consente un controllo preciso sui risultati della ricerca, ma potrebbe non " +"mostrare risultati se vengono commessi troppi errori." #: .\cookbook\templates\settings.html:44 msgid "Perfect for large Databases" -msgstr "Perfetto per database grandi" +msgstr "Ideale per database grandi" #: .\cookbook\templates\setup.html:6 .\cookbook\templates\system.html:5 msgid "Cookbook Setup" @@ -1918,10 +1952,8 @@ msgstr "Crea super utente" #: .\cookbook\templates\socialaccount\authentication_error.html:7 #: .\cookbook\templates\socialaccount\authentication_error.html:23 -#, fuzzy -#| msgid "Social Login" msgid "Social Network Login Failure" -msgstr "Login con social network" +msgstr "Errore di login con Social Network" #: .\cookbook\templates\socialaccount\authentication_error.html:25 msgid "" @@ -2210,266 +2242,266 @@ msgstr "" msgid "URL Import" msgstr "Importa da URL" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "Il parametro updated_at non è formattato correttamente" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "Non esiste nessun {self.basename} con id {pk}" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "Non è possibile unirlo con lo stesso oggetto!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "Non esiste nessun {self.basename} con id {target}" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "Non è possibile unirlo con un oggetto secondario!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} è stato unito con successo a {target.name}" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" "Si è verificato un errore durante l'unione di {source.name} con {target.name}" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} è stato spostato con successo alla radice." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Si è verificato un errore durante lo spostamento " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "Non è possibile muovere un oggetto a sé stesso!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "Non esiste nessun {self.basename} con id {parent}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} è stato spostato con successo al primario {parent.name}" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} è stato rimosso dalla lista della spesa." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} è stato aggiunto alla lista della spesa." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" "ID di una ricetta di cui uno step ne fa parte. Usato per parametri di " "ripetizione multipla." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "Stringa di ricerca abbinata (vaga) al nome dell'oggetto." -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" "Filtra le ricette che possono essere preparate con alimenti già disponibili. " "[true/false]" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" "Restituisce le voci della lista della spesa ordinate per categoria di " "supermercato." -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Nulla da fare." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "URL non valido" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Connessione rifiutata." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "Schema URL invalido." -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "Nessuna informazione utilizzabile è stata trovata." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "Questo provider non permette l'importazione" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" "Questa funzione non è ancora disponibile nella versione hostata di Tandor!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Sincronizzazione completata con successo!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Errore di sincronizzazione con questo backend" @@ -2535,7 +2567,7 @@ msgstr "Modifiche salvate!" msgid "Error saving changes!" msgstr "Si è verificato un errore durante il salvataggio delle modifiche!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2583,7 +2615,12 @@ msgstr "La nuova ricetta è stata importata!" msgid "There was an error importing this recipe!" msgstr "Si è verificato un errore durante l'importazione di questa ricetta!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Questa funzione non è disponibile nella versione demo!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2591,24 +2628,19 @@ msgstr "" "Hai creato la tua istanza personale per le ricette. Inizia aggiungendo " "qualche ricetta o invita altre persone a unirsi a te." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "Non hai i permessi necessari per completare questa operazione!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Commento salvato!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Questa funzione non è disponibile nella versione demo!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "Devi selezionare almeno un campo da cercare!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2616,11 +2648,11 @@ msgstr "" "Per utilizzare questo metodo di ricerca devi selezionare almeno un campo di " "ricerca full text!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "La ricerca vaga non è compatibile con questo metodo di ricerca!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2630,27 +2662,27 @@ msgstr "" "utente! Se hai dimenticato le credenziali del tuo super utente controlla la " "documentazione di Django per resettare le password." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Le password non combaciano!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "L'utente è stato creato e ora può essere usato per il login!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "È stato fornito un link di invito non valido!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Sei entrato a far parte di questa istanza." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "Il link di invito non è valido o è stato già usato!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2658,7 +2690,7 @@ msgstr "" "La segnalazione dei link di condivisione non è abilitata per questa istanza. " "Notifica l'amministratore per segnalare i problemi." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/lv/LC_MESSAGES/django.po b/cookbook/locale/lv/LC_MESSAGES/django.po index aab3f14fc..8a5e7295e 100644 --- a/cookbook/locale/lv/LC_MESSAGES/django.po +++ b/cookbook/locale/lv/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" "PO-Revision-Date: 2023-01-08 17:55+0000\n" "Last-Translator: Joachim Weber \n" "Language-Team: Latvian /remote." "php/webdav/ is added automatically)" @@ -217,33 +217,33 @@ msgstr "" "Atstājiet tukšu Dropbox un ievadiet tikai Nextcloud bāzes URL ( /" "remote.php/webdav/ tiek pievienots automātiski)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Krātuve" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Meklēšanas virkne" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "Faila ID" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Jums jānorāda vismaz recepte vai nosaukums." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Iestatījumos varat uzskaitīt noklusējuma lietotājus, ar kuriem koplietot " "receptes." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -251,219 +251,219 @@ msgstr "" "Lai formatētu šo lauku, varat izmantot Markdown. Skatiet dokumentus šeit " -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "" -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "" -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 #, fuzzy #| msgid "Search" msgid "Search Method" msgstr "Meklēt" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 #, fuzzy #| msgid "Search" msgid "Fuzzy Search" msgstr "Meklēt" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 #, fuzzy #| msgid "Text" msgid "Full Text" msgstr "Teskts" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 #, fuzzy #| msgid "Shopping List" msgid "Share Shopping List" msgstr "Iepirkumu saraksts" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Saraksta prefikss" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 #, fuzzy #| msgid "Food that should be replaced." msgid "Fields on food that should be inherited by default." msgstr "Ēdiens, kas būtu jāaizstāj." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 #, fuzzy #| msgid "Show recently viewed recipes on search page." msgid "Show recipe counts on search filters" msgstr "Parādīt nesen skatītās receptes meklēšanas lapā." -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -474,7 +474,7 @@ msgid "" msgstr "" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Jūs neesat pieteicies un tāpēc nevarat skatīt šo lapu!" @@ -487,7 +487,7 @@ msgstr "Jūs neesat pieteicies un tāpēc nevarat skatīt šo lapu!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Jums nav nepieciešamo atļauju, lai apskatītu šo lapu!" @@ -506,11 +506,41 @@ msgstr "" msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "System Information" +msgid "reverse rotation" +msgstr "Sistēmas informācija" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 #, fuzzy #| msgid "You must provide at least a recipe or a title." msgid "You must supply a servings size" @@ -530,40 +560,46 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "Importētas %s receptes." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +#, fuzzy +#| msgid "Recipe Home" +msgid "Recipe source:" +msgstr "Recepšu Sākums" + +#: .\cookbook\integration\paprika.py:49 #, fuzzy #| msgid "Note" msgid "Notes" msgstr "Piezīme" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 #, fuzzy #| msgid "Information" msgid "Nutritional Information" msgstr "Informācija" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "" @@ -630,82 +666,82 @@ msgid "" "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Meklēt" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Maltīšu plāns" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Grāmatas" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr "" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Food" msgid "Food Alias" msgstr "Ēdiens" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Units" msgid "Unit Alias" msgstr "Vienības" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Keywords" msgid "Keyword Alias" msgstr "Atslēgvārdi" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Instructions" msgid "Instruction Replace" msgstr "Instrukcijas" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Recepte" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 #, fuzzy #| msgid "Food" msgid "Food" msgstr "Ēdiens" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Atslēgvārds" @@ -721,64 +757,64 @@ msgstr "" msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "" -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr "" -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "" -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "" -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1640,11 +1676,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "pēc" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Komentēt" @@ -2170,262 +2206,262 @@ msgstr "" msgid "URL Import" msgstr "URL importēšana" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 #, fuzzy #| msgid "Parameter filter_list incorrectly formatted" msgid "Parameter updated_at incorrectly formatted" msgstr "Parametrs filter_list ir nepareizi formatēts" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 #, fuzzy #| msgid "The requested page could not be found." msgid "No usable data could be found." msgstr "Pieprasīto lapu nevarēja atrast." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Sinhronizācija ir veiksmīga!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Sinhronizējot ar krātuvi, radās kļūda" @@ -2489,7 +2525,7 @@ msgstr "Izmaiņas saglabātas!" msgid "Error saving changes!" msgstr "Saglabājot izmaiņas, radās kļūda!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2539,40 +2575,40 @@ msgstr "Importēta jauna recepte!" msgid "There was an error importing this recipe!" msgstr "Importējot šo recepti, radās kļūda!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "Jums nav nepieciešamo atļauju, lai veiktu šo darbību!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Komentārs saglabāts!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2582,33 +2618,33 @@ msgstr "" "aizmirsis sava superlietotāja informāciju, lūdzu, skatiet Django " "dokumentāciju par paroļu atiestatīšanu." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Paroles nesakrīt!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "Lietotājs ir izveidots, lūdzu, piesakieties!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "Nepareiza uzaicinājuma saite!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "Uzaicinājuma saite nav derīga vai jau izmantota!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/nb_NO/LC_MESSAGES/django.mo b/cookbook/locale/nb_NO/LC_MESSAGES/django.mo index 599d45ea3..94b701688 100644 Binary files a/cookbook/locale/nb_NO/LC_MESSAGES/django.mo and b/cookbook/locale/nb_NO/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/nb_NO/LC_MESSAGES/django.po b/cookbook/locale/nb_NO/LC_MESSAGES/django.po index 07179da11..efd5c85a4 100644 --- a/cookbook/locale/nb_NO/LC_MESSAGES/django.po +++ b/cookbook/locale/nb_NO/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-04-11 15:09+0200\n" -"PO-Revision-Date: 2021-04-11 15:23+0000\n" -"Last-Translator: Allan Nordhøy \n" +"PO-Revision-Date: 2023-08-19 21:36+0000\n" +"Last-Translator: NeoID \n" "Language-Team: Norwegian Bokmål \n" "Language: nb_NO\n" @@ -17,7 +17,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.5.3\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\base.html:91 #: .\cookbook\templates\forms\edit_internal_recipe.html:219 @@ -31,22 +31,28 @@ msgid "" "Color of the top navigation bar. Not all colors work with all themes, just " "try them out!" msgstr "" +"Farge på toppnavigasjonslinjen. Ikke alle farger fungerer med alle temaer, " +"så bare prøv dem ut!" #: .\cookbook\forms.py:46 msgid "Default Unit to be used when inserting a new ingredient into a recipe." -msgstr "" +msgstr "Standard enhet når ny ingrediens legges til en oppskrift." #: .\cookbook\forms.py:47 msgid "" "Enables support for fractions in ingredient amounts (e.g. convert decimals " "to fractions automatically)" msgstr "" +"Aktiverer støtte for deler av ingrediensmengde (konverterer feks. desimaler " +"til deler automatisk)" #: .\cookbook\forms.py:48 msgid "" "Users with whom newly created meal plan/shopping list entries should be " "shared by default." msgstr "" +"Brukere som oppretter nye måltidsplaner/handlelister, deler disse " +"oppføringene som standard." #: .\cookbook\forms.py:49 msgid "Show recently viewed recipes on search page." @@ -58,7 +64,7 @@ msgstr "Antall desimaler ingredienser skal avrundes til." #: .\cookbook\forms.py:51 msgid "If you want to be able to create and see comments underneath recipes." -msgstr "" +msgstr "Hvis du ønsker å opprette og se kommentarer under oppskrifter." #: .\cookbook\forms.py:53 msgid "" @@ -67,16 +73,23 @@ msgid "" "Useful when shopping with multiple people but might use a little bit of " "mobile data. If lower than instance limit it is reset when saving." msgstr "" +"0 vil deaktivere automatisk synkronisering. Når en handleliste vises, " +"oppdateres listen med oppgitt antall sekunders mellomrom for å synkronisere " +"endringer fra andre brukere. Nyttig dersom flere brukere handler samtidig. " +"Datatrafikk oppstår når aktiv. Hvis verdien er lavere enn grensen, " +"tilbakestilles den ved lagring." #: .\cookbook\forms.py:56 msgid "Makes the navbar stick to the top of the page." -msgstr "" +msgstr "Fest navigasjonslinjen til toppen av siden." #: .\cookbook\forms.py:72 msgid "" "Both fields are optional. If none are given the username will be displayed " "instead" msgstr "" +"Begge feltene er valgfrie. Hvis ingen blir oppgitt, vil brukernavnet vises i " +"stedet" #: .\cookbook\forms.py:93 .\cookbook\forms.py:315 #: .\cookbook\templates\forms\edit_internal_recipe.html:45 @@ -88,23 +101,23 @@ msgstr "Navn" #: .\cookbook\templates\forms\edit_internal_recipe.html:81 #: .\cookbook\templates\stats.html:24 .\cookbook\templates\url_import.html:202 msgid "Keywords" -msgstr "" +msgstr "Nøkkelord" #: .\cookbook\forms.py:95 msgid "Preparation time in minutes" -msgstr "" +msgstr "Forberedelsestid i minutter" #: .\cookbook\forms.py:96 msgid "Waiting time (cooking/baking) in minutes" -msgstr "" +msgstr "Ventetid (til matlaging/baking) i minutter" #: .\cookbook\forms.py:97 .\cookbook\forms.py:317 msgid "Path" -msgstr "" +msgstr "Sti" #: .\cookbook\forms.py:98 msgid "Storage UID" -msgstr "" +msgstr "Lagring UID" #: .\cookbook\forms.py:121 msgid "Default" @@ -115,6 +128,8 @@ msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." msgstr "" +"For å unngå duplikater, blir oppskrifter med samme navn som eksisterende " +"ignorert. Merk av denne boksen for å importere alt." #: .\cookbook\forms.py:149 msgid "New Unit" @@ -122,32 +137,31 @@ msgstr "Ny enhet" #: .\cookbook\forms.py:150 msgid "New unit that other gets replaced by." -msgstr "" +msgstr "Ny enhet som erstatter den gamle." #: .\cookbook\forms.py:155 msgid "Old Unit" msgstr "Gammel enhet" #: .\cookbook\forms.py:156 -#, fuzzy msgid "Unit that should be replaced." msgstr "Enhet som skal erstattes." #: .\cookbook\forms.py:172 msgid "New Food" -msgstr "" +msgstr "Ny matvare" #: .\cookbook\forms.py:173 msgid "New food that other gets replaced by." -msgstr "" +msgstr "Ny matvare som erstatter den gamle." #: .\cookbook\forms.py:178 msgid "Old Food" -msgstr "" +msgstr "Gammel matvare" #: .\cookbook\forms.py:179 msgid "Food that should be replaced." -msgstr "" +msgstr "Matvare som bør erstattes." #: .\cookbook\forms.py:197 msgid "Add your comment: " @@ -155,17 +169,19 @@ msgstr "Legg til din kommentar: " #: .\cookbook\forms.py:238 msgid "Leave empty for dropbox and enter app password for nextcloud." -msgstr "" +msgstr "La det stå tomt for Dropbox og skriv inn app-passordet for Nextcloud." #: .\cookbook\forms.py:245 msgid "Leave empty for nextcloud and enter api token for dropbox." -msgstr "" +msgstr "La det stå tomt for Nextcloud og skriv inn API-tokenet for Dropbox." #: .\cookbook\forms.py:253 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" msgstr "" +"La det stå tomt for Dropbox, og skriv bare inn grunn-URLen for Nextcloud " +"(/remote.php/webdav/ blir lagt til automatisk)" #: .\cookbook\forms.py:291 msgid "Search String" @@ -177,11 +193,12 @@ msgstr "Fil-ID" #: .\cookbook\forms.py:354 msgid "You must provide at least a recipe or a title." -msgstr "" +msgstr "Du må oppgi minst en oppskrift eller en tittel." #: .\cookbook\forms.py:367 msgid "You can list default users to share recipes with in the settings." msgstr "" +"Du kan liste opp standardbrukere for å dele oppskrifter innen innstillingene." #: .\cookbook\forms.py:368 #: .\cookbook\templates\forms\edit_internal_recipe.html:377 @@ -189,10 +206,14 @@ msgid "" "You can use markdown to format this field. See the docs here" msgstr "" +"Du kan bruke Markdown for å formatere dette feltet. Se dokumentasjonen her" #: .\cookbook\forms.py:393 msgid "A username is not required, if left blank the new user can choose one." msgstr "" +"Et brukernavn er ikke påkrevd. Hvis det blir stående tomt, kan den nye " +"brukeren velge ett selv." #: .\cookbook\helper\permission_helper.py:123 #: .\cookbook\helper\permission_helper.py:129 @@ -204,37 +225,40 @@ msgstr "" #: .\cookbook\views\views.py:112 .\cookbook\views\views.py:116 #: .\cookbook\views\views.py:184 msgid "You do not have the required permissions to view this page!" -msgstr "Du har ikke påkrevd tilgang for å vise denne siden." +msgstr "Du har ikke påkrevd tilgang for å vise denne siden!" #: .\cookbook\helper\permission_helper.py:141 -#, fuzzy msgid "You are not logged in and therefore cannot view this page!" -msgstr "Du er ikke innlogget og kan derfor ikke vise siden." +msgstr "Du er ikke innlogget og kan derfor ikke vise siden!" #: .\cookbook\helper\permission_helper.py:145 #: .\cookbook\helper\permission_helper.py:167 #: .\cookbook\helper\permission_helper.py:182 msgid "You cannot interact with this object as it is not owned by you!" -msgstr "" +msgstr "Du kan ikke samhandle med dette objektet, da det ikke tilhører deg!" #: .\cookbook\helper\recipe_url_import.py:40 .\cookbook\views\api.py:549 msgid "The requested site provided malformed data and cannot be read." msgstr "" +"Nettstedet du har forespurt, har levert feilformatert data som ikke kan " +"leses." #: .\cookbook\helper\recipe_url_import.py:54 msgid "" "The requested site does not provide any recognized data format to import the " "recipe from." msgstr "" +"Det forespurte nettstedet gir ingen gjenkjennelig dataformat som kan " +"importeres oppskriften fra." #: .\cookbook\helper\recipe_url_import.py:160 msgid "Imported from" -msgstr "" +msgstr "Importert fra" #: .\cookbook\helper\template_helper.py:60 #: .\cookbook\helper\template_helper.py:62 msgid "Could not parse template code." -msgstr "" +msgstr "Kunne ikke analysere mal-koden." #: .\cookbook\integration\integration.py:102 #: .\cookbook\templates\import.html:14 .\cookbook\templates\import.html:20 @@ -243,50 +267,52 @@ msgstr "" #: .\cookbook\templates\url_import.html:233 .\cookbook\views\delete.py:60 #: .\cookbook\views\edit.py:190 msgid "Import" -msgstr "" +msgstr "Importér" #: .\cookbook\integration\integration.py:131 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" +"Importøren forventet en .zip-fil. Har du valgt riktig type importør for " +"dataene dine?" #: .\cookbook\integration\integration.py:134 msgid "The following recipes were ignored because they already existed:" -msgstr "" +msgstr "Følgende oppskrifter ble ignorert fordi de allerede eksisterte:" #: .\cookbook\integration\integration.py:137 #, python-format msgid "Imported %s recipes." -msgstr "" +msgstr "Importerte %s oppskrifter." #: .\cookbook\integration\paprika.py:44 msgid "Notes" -msgstr "" +msgstr "Notater" #: .\cookbook\integration\paprika.py:47 msgid "Nutritional Information" -msgstr "" +msgstr "Næringsinformasjon" #: .\cookbook\integration\paprika.py:50 msgid "Source" -msgstr "" +msgstr "Kilde" #: .\cookbook\integration\safron.py:23 #: .\cookbook\templates\forms\edit_internal_recipe.html:75 #: .\cookbook\templates\include\log_cooking.html:16 #: .\cookbook\templates\url_import.html:84 msgid "Servings" -msgstr "" +msgstr "Porsjoner" #: .\cookbook\integration\safron.py:25 msgid "Waiting time" -msgstr "" +msgstr "Ventetid" #: .\cookbook\integration\safron.py:27 #: .\cookbook\templates\forms\edit_internal_recipe.html:69 msgid "Preparation Time" -msgstr "" +msgstr "Forberedelsestid" #: .\cookbook\integration\safron.py:29 .\cookbook\templates\base.html:71 #: .\cookbook\templates\forms\ingredients.html:7 @@ -322,7 +348,7 @@ msgstr "Søk" #: .\cookbook\templates\meal_plan.html:5 .\cookbook\views\delete.py:152 #: .\cookbook\views\edit.py:224 .\cookbook\views\new.py:188 msgid "Meal-Plan" -msgstr "" +msgstr "Måltidsplan" #: .\cookbook\models.py:112 .\cookbook\templates\base.html:82 msgid "Books" @@ -330,11 +356,11 @@ msgstr "Bøker" #: .\cookbook\models.py:119 msgid "Small" -msgstr "" +msgstr "Liten" #: .\cookbook\models.py:119 msgid "Large" -msgstr "" +msgstr "Stor" #: .\cookbook\models.py:327 #: .\cookbook\templates\forms\edit_internal_recipe.html:198 @@ -379,7 +405,7 @@ msgstr "Finner ikke siden du leter etter." #: .\cookbook\templates\404.html:33 msgid "Take me Home" -msgstr "" +msgstr "Tilbake til Startsiden" #: .\cookbook\templates\404.html:35 msgid "Report a Bug" @@ -388,12 +414,12 @@ msgstr "Rapporter en feil" #: .\cookbook\templates\account\login.html:7 #: .\cookbook\templates\base.html:170 msgid "Login" -msgstr "" +msgstr "Logg inn" #: .\cookbook\templates\account\login.html:13 #: .\cookbook\templates\account\login.html:28 msgid "Sign In" -msgstr "" +msgstr "Opprett bruker" #: .\cookbook\templates\account\login.html:38 msgid "Social Login" @@ -401,7 +427,7 @@ msgstr "Sosial innlogging" #: .\cookbook\templates\account\login.html:39 msgid "You can use any of the following providers to sign in." -msgstr "" +msgstr "Velg en av følgende leverandører for å logge på." #: .\cookbook\templates\account\logout.html:5 #: .\cookbook\templates\account\logout.html:9 @@ -416,20 +442,20 @@ msgstr "Er du sikker på at du vil logge ut?" #: .\cookbook\templates\account\password_reset.html:5 #: .\cookbook\templates\account\password_reset_done.html:5 msgid "Password Reset" -msgstr "" +msgstr "Nullstill passord" #: .\cookbook\templates\account\password_reset.html:9 #: .\cookbook\templates\account\password_reset_done.html:9 msgid "Password reset is not implemented for the time being!" -msgstr "" +msgstr "Det er foreløpig ikke implementert funksjon for å nullstille passord!" #: .\cookbook\templates\account\signup.html:5 msgid "Register" -msgstr "" +msgstr "Registrer" #: .\cookbook\templates\account\signup.html:9 msgid "Create your Account" -msgstr "Opprett din konto" +msgstr "Opprett konto" #: .\cookbook\templates\account\signup.html:14 msgid "Create User" @@ -442,11 +468,11 @@ msgstr "API-dokumentasjon" #: .\cookbook\templates\base.html:78 msgid "Utensils" -msgstr "" +msgstr "Redskaper" #: .\cookbook\templates\base.html:88 msgid "Shopping" -msgstr "" +msgstr "Handle" #: .\cookbook\templates\base.html:102 .\cookbook\views\delete.py:84 #: .\cookbook\views\edit.py:93 .\cookbook\views\lists.py:26 @@ -456,27 +482,27 @@ msgstr "Nøkkelord" #: .\cookbook\templates\base.html:104 msgid "Batch Edit" -msgstr "" +msgstr "Oppdatere flere" #: .\cookbook\templates\base.html:109 msgid "Storage Data" -msgstr "" +msgstr "Datalagring" #: .\cookbook\templates\base.html:113 msgid "Storage Backends" -msgstr "" +msgstr "Lagringsplasser" #: .\cookbook\templates\base.html:115 msgid "Configure Sync" -msgstr "" +msgstr "Konfigurer synkronisering" #: .\cookbook\templates\base.html:117 msgid "Discovered Recipes" -msgstr "" +msgstr "Oppdagede oppskrifter" #: .\cookbook\templates\base.html:119 msgid "Discovery Log" -msgstr "" +msgstr "Logg Oppdagelser" #: .\cookbook\templates\base.html:121 .\cookbook\templates\stats.html:10 msgid "Statistics" @@ -484,7 +510,7 @@ msgstr "Statistikk" #: .\cookbook\templates\base.html:123 msgid "Units & Ingredients" -msgstr "" +msgstr "Enheter & Ingredienser" #: .\cookbook\templates\base.html:125 msgid "Import Recipe" @@ -521,58 +547,61 @@ msgid "API Browser" msgstr "API-utforsker" #: .\cookbook\templates\base.html:165 -#, fuzzy msgid "Logout" msgstr "Logg ut" #: .\cookbook\templates\batch\edit.html:6 msgid "Batch edit Category" -msgstr "" +msgstr "Oppdater flere kategorier" #: .\cookbook\templates\batch\edit.html:15 msgid "Batch edit Recipes" -msgstr "" +msgstr "Oppdater flere oppskrifter" #: .\cookbook\templates\batch\edit.html:20 msgid "Add the specified keywords to all recipes containing a word" -msgstr "" +msgstr "Legg til spesifikt nøkkelord til alle oppskrifter som inneholder et ord" #: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:76 msgid "Sync" -msgstr "" +msgstr "Synkronisering" #: .\cookbook\templates\batch\monitor.html:10 msgid "Manage watched Folders" -msgstr "" +msgstr "Behandle overvåkede mapper" #: .\cookbook\templates\batch\monitor.html:14 msgid "" "On this Page you can manage all storage folder locations that should be " "monitored and synced." msgstr "" +"Her kan du behandle alle lagringsmapper og plasseringer for monitorering og " +"synkronisering." #: .\cookbook\templates\batch\monitor.html:16 msgid "The path must be in the following format" -msgstr "" +msgstr "Stien må være i følgende format" #: .\cookbook\templates\batch\monitor.html:27 msgid "Sync Now!" -msgstr "" +msgstr "Synkroniser nå!" #: .\cookbook\templates\batch\waiting.html:4 #: .\cookbook\templates\batch\waiting.html:10 msgid "Importing Recipes" -msgstr "" +msgstr "Importerer oppskrifter" #: .\cookbook\templates\batch\waiting.html:23 msgid "" "This can take a few minutes, depending on the number of recipes in sync, " "please wait." msgstr "" +"Dette kan ta noen minutter, avhenging av antall oppskrifter som skal " +"synkroniseres. Vennligst vent." #: .\cookbook\templates\books.html:5 .\cookbook\templates\books.html:11 msgid "Recipe Books" -msgstr "" +msgstr "Oppskriftsbøker" #: .\cookbook\templates\books.html:15 msgid "New Book" @@ -584,32 +613,32 @@ msgstr "av" #: .\cookbook\templates\books.html:34 msgid "Toggle Recipes" -msgstr "" +msgstr "Veksle oppskrifter" #: .\cookbook\templates\books.html:54 #: .\cookbook\templates\meal_plan_entry.html:48 #: .\cookbook\templates\recipes_table.html:64 msgid "Last cooked" -msgstr "" +msgstr "Forrige tilbereding" #: .\cookbook\templates\books.html:71 msgid "There are no recipes in this book yet." -msgstr "" +msgstr "Det er foreløpig ingen oppskrifter i denne boken." #: .\cookbook\templates\export.html:6 .\cookbook\templates\test2.html:6 msgid "Export Recipes" -msgstr "" +msgstr "Eksporter oppskrifter" #: .\cookbook\templates\export.html:14 .\cookbook\templates\export.html:20 #: .\cookbook\templates\shopping_list.html:347 #: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20 msgid "Export" -msgstr "" +msgstr "Eksporter" #: .\cookbook\templates\forms\edit_import_recipe.html:5 #: .\cookbook\templates\forms\edit_import_recipe.html:9 msgid "Import new Recipe" -msgstr "" +msgstr "Importer ny oppskrift" #: .\cookbook\templates\forms\edit_import_recipe.html:14 #: .\cookbook\templates\forms\edit_internal_recipe.html:389 @@ -635,29 +664,29 @@ msgstr "Beskrivelse" #: .\cookbook\templates\forms\edit_internal_recipe.html:72 msgid "Waiting Time" -msgstr "" +msgstr "Ventetid" #: .\cookbook\templates\forms\edit_internal_recipe.html:78 msgid "Servings Text" -msgstr "" +msgstr "Porsjon beskrivelse" #: .\cookbook\templates\forms\edit_internal_recipe.html:89 msgid "Select Keywords" -msgstr "" +msgstr "Velg nøkkelord" #: .\cookbook\templates\forms\edit_internal_recipe.html:90 #: .\cookbook\templates\url_import.html:212 msgid "Add Keyword" -msgstr "" +msgstr "Legg til nøkkelord" #: .\cookbook\templates\forms\edit_internal_recipe.html:108 msgid "Nutrition" -msgstr "" +msgstr "Næringsinnhold" #: .\cookbook\templates\forms\edit_internal_recipe.html:112 #: .\cookbook\templates\forms\edit_internal_recipe.html:162 msgid "Delete Step" -msgstr "" +msgstr "Fjern trinn" #: .\cookbook\templates\forms\edit_internal_recipe.html:116 msgid "Calories" @@ -678,15 +707,15 @@ msgstr "Proteiner" #: .\cookbook\templates\forms\edit_internal_recipe.html:146 #: .\cookbook\templates\forms\edit_internal_recipe.html:454 msgid "Step" -msgstr "" +msgstr "Trinn" #: .\cookbook\templates\forms\edit_internal_recipe.html:167 msgid "Show as header" -msgstr "" +msgstr "Vis som overskrift" #: .\cookbook\templates\forms\edit_internal_recipe.html:173 msgid "Hide as header" -msgstr "" +msgstr "Skjul overskrift" #: .\cookbook\templates\forms\edit_internal_recipe.html:178 msgid "Move Up" @@ -698,15 +727,15 @@ msgstr "Flytt nedover" #: .\cookbook\templates\forms\edit_internal_recipe.html:192 msgid "Step Name" -msgstr "" +msgstr "Trinn navn" #: .\cookbook\templates\forms\edit_internal_recipe.html:196 msgid "Step Type" -msgstr "" +msgstr "Trinn type" #: .\cookbook\templates\forms\edit_internal_recipe.html:207 msgid "Step time in Minutes" -msgstr "" +msgstr "Trinn tid i minutter" #: .\cookbook\templates\forms\edit_internal_recipe.html:261 #: .\cookbook\templates\shopping_list.html:183 @@ -740,7 +769,7 @@ msgstr "Velg mat" #: .\cookbook\templates\meal_plan.html:256 #: .\cookbook\templates\url_import.html:171 msgid "Note" -msgstr "" +msgstr "Notis" #: .\cookbook\templates\forms\edit_internal_recipe.html:319 msgid "Delete Ingredient" @@ -748,7 +777,7 @@ msgstr "Slett ingrediens" #: .\cookbook\templates\forms\edit_internal_recipe.html:325 msgid "Make Header" -msgstr "" +msgstr "Bruk som overskrift" #: .\cookbook\templates\forms\edit_internal_recipe.html:331 msgid "Make Ingredient" @@ -756,15 +785,15 @@ msgstr "Opprett ingrediens" #: .\cookbook\templates\forms\edit_internal_recipe.html:337 msgid "Disable Amount" -msgstr "" +msgstr "Deaktiver mengde" #: .\cookbook\templates\forms\edit_internal_recipe.html:343 msgid "Enable Amount" -msgstr "" +msgstr "Aktiver mengde" #: .\cookbook\templates\forms\edit_internal_recipe.html:348 msgid "Copy Template Reference" -msgstr "" +msgstr "Kopier mal-referanse" #: .\cookbook\templates\forms\edit_internal_recipe.html:374 #: .\cookbook\templates\url_import.html:196 @@ -773,29 +802,28 @@ msgstr "Instruksjoner" #: .\cookbook\templates\forms\edit_internal_recipe.html:387 #: .\cookbook\templates\forms\edit_internal_recipe.html:418 -#, fuzzy msgid "Save & View" msgstr "Lagre og vis" #: .\cookbook\templates\forms\edit_internal_recipe.html:391 #: .\cookbook\templates\forms\edit_internal_recipe.html:424 msgid "Add Step" -msgstr "" +msgstr "Legg til trinn" #: .\cookbook\templates\forms\edit_internal_recipe.html:394 #: .\cookbook\templates\forms\edit_internal_recipe.html:428 msgid "Add Nutrition" -msgstr "" +msgstr "Legg til næringsinnhold" #: .\cookbook\templates\forms\edit_internal_recipe.html:396 #: .\cookbook\templates\forms\edit_internal_recipe.html:430 msgid "Remove Nutrition" -msgstr "" +msgstr "Fjern næringsinnhold" #: .\cookbook\templates\forms\edit_internal_recipe.html:398 #: .\cookbook\templates\forms\edit_internal_recipe.html:433 msgid "View Recipe" -msgstr "" +msgstr "Vis oppskrift" #: .\cookbook\templates\forms\edit_internal_recipe.html:400 #: .\cookbook\templates\forms\edit_internal_recipe.html:435 @@ -804,11 +832,11 @@ msgstr "Slett oppskrift" #: .\cookbook\templates\forms\edit_internal_recipe.html:441 msgid "Steps" -msgstr "" +msgstr "Trinn" #: .\cookbook\templates\forms\ingredients.html:15 msgid "Edit Ingredients" -msgstr "" +msgstr "Rediger ingrediens" #: .\cookbook\templates\forms\ingredients.html:16 msgid "" @@ -820,54 +848,61 @@ msgid "" "them.\n" " " msgstr "" +"\n" +" Følgende skjema kan brukes dersom, tilfeldigvis, to eller flere " +"enheter eller ingredienser er opprettet,\n" +" og burde være identiske.\n" +" Det slår sammen to enheter eller ingredienser og oppdaterer alle " +"oppskrifter som inneholder disse.\n" +" " #: .\cookbook\templates\forms\ingredients.html:24 #: .\cookbook\templates\stats.html:26 msgid "Units" -msgstr "" +msgstr "Enheter" #: .\cookbook\templates\forms\ingredients.html:26 msgid "Are you sure that you want to merge these two units?" -msgstr "" +msgstr "Er du sikker på at du vil slå sammen disse enhetene?" #: .\cookbook\templates\forms\ingredients.html:31 #: .\cookbook\templates\forms\ingredients.html:40 msgid "Merge" -msgstr "Flett" +msgstr "Slå sammen" #: .\cookbook\templates\forms\ingredients.html:36 msgid "Are you sure that you want to merge these two ingredients?" -msgstr "" +msgstr "Er du sikker på at du vil slå sammen disse ingrediensene?" #: .\cookbook\templates\generic\delete_template.html:18 #, python-format msgid "Are you sure you want to delete the %(title)s: %(object)s " -msgstr "" +msgstr "Er du sikker på at du vil slette %(title)s: %(object)s " #: .\cookbook\templates\generic\delete_template.html:21 msgid "Confirm" -msgstr "" +msgstr "Bekreft" #: .\cookbook\templates\generic\edit_template.html:30 msgid "View" -msgstr "" +msgstr "Vis" #: .\cookbook\templates\generic\edit_template.html:34 msgid "Delete original file" -msgstr "" +msgstr "Slett opprinnelig fil" #: .\cookbook\templates\generic\list_template.html:6 #: .\cookbook\templates\generic\list_template.html:12 msgid "List" -msgstr "" +msgstr "Liste" #: .\cookbook\templates\generic\list_template.html:25 msgid "Filter" -msgstr "" +msgstr "Filtrer" #: .\cookbook\templates\generic\list_template.html:30 msgid "Import all" -msgstr "" +msgstr "Importer alle" #: .\cookbook\templates\generic\new_template.html:6 #: .\cookbook\templates\generic\new_template.html:14 @@ -891,19 +926,19 @@ msgstr "Vis logg" #: .\cookbook\templates\history.html:24 msgid "Cook Log" -msgstr "" +msgstr "Tilberedingslogg" #: .\cookbook\templates\import.html:6 .\cookbook\templates\test.html:6 msgid "Import Recipes" -msgstr "" +msgstr "Importer oppskrifter" #: .\cookbook\templates\include\log_cooking.html:7 msgid "Log Recipe Cooking" -msgstr "" +msgstr "Loggfør tilberedt oppskrift" #: .\cookbook\templates\include\log_cooking.html:13 msgid "All fields are optional and can be left empty." -msgstr "" +msgstr "Alle felt er valgfri og kan stå tomme." #: .\cookbook\templates\include\log_cooking.html:19 msgid "Rating" @@ -943,44 +978,53 @@ msgid "" "can be used.\n" " " msgstr "" +"\n" +" Passord og nøkkelfeltene er lagret som ren tekst i " +"databasen.\n" +" Dette er nødvendig for å kunne utføre API-forespørsler, men det øker " +"samtidig risiko for\n" +" uønsket tilgang til dem.
    \n" +" For å begrense kosekvensene av uønsket tilgang, kan nøkler eller " +"kontoer med begrenset tilgang benyttes.\n" +" " #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "" +msgstr "Søk etter oppskrift..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" -msgstr "" +msgstr "Ny oppskrift" #: .\cookbook\templates\index.html:47 msgid "Website Import" -msgstr "" +msgstr "Importer fra nettside" #: .\cookbook\templates\index.html:53 msgid "Advanced Search" -msgstr "" +msgstr "Avansert søk" #: .\cookbook\templates\index.html:57 msgid "Reset Search" -msgstr "" +msgstr "Nullstill søk" #: .\cookbook\templates\index.html:85 msgid "Last viewed" -msgstr "" +msgstr "Sist sett" #: .\cookbook\templates\index.html:87 .\cookbook\templates\meal_plan.html:178 #: .\cookbook\templates\stats.html:22 msgid "Recipes" -msgstr "" +msgstr "Oppskrifter" #: .\cookbook\templates\index.html:94 msgid "Log in to view recipes" -msgstr "" +msgstr "Logg inn for å se oppskrifter" #: .\cookbook\templates\markdown_info.html:5 #: .\cookbook\templates\markdown_info.html:13 msgid "Markdown Info" -msgstr "" +msgstr "Markdown informasjon" #: .\cookbook\templates\markdown_info.html:14 msgid "" @@ -997,43 +1041,56 @@ msgid "" "below.\n" " " msgstr "" +"\n" +" Markdown er et lettvekts markup språk som benyttes for å formatere " +"ren tekst.\n" +" Denne siden bruker biblioteket Python Markdown for\n" +" å konvertere teksten din til velformatert HTML. Fullstendig " +"dokumentasjon for markdown finner du\n" +" her.\n" +" En ufullstendig, men sannsynligvis tilstrekkelig dokumentasjon " +"finner du under her.\n" +" " #: .\cookbook\templates\markdown_info.html:25 msgid "Headers" -msgstr "" +msgstr "Overskrifter" #: .\cookbook\templates\markdown_info.html:54 msgid "Formatting" -msgstr "" +msgstr "Formatering" #: .\cookbook\templates\markdown_info.html:56 #: .\cookbook\templates\markdown_info.html:72 msgid "Line breaks are inserted by adding two spaces after the end of a line" msgstr "" +"Linjeskift er satt inn ved å sette inn to mellomrom på slutten av en linje" #: .\cookbook\templates\markdown_info.html:57 #: .\cookbook\templates\markdown_info.html:73 msgid "or by leaving a blank line inbetween." -msgstr "" +msgstr "eller ved å sette inn en tom linje mellom." #: .\cookbook\templates\markdown_info.html:59 #: .\cookbook\templates\markdown_info.html:74 msgid "This text is bold" -msgstr "" +msgstr "Denne teksten er Fet" #: .\cookbook\templates\markdown_info.html:60 #: .\cookbook\templates\markdown_info.html:75 msgid "This text is italic" -msgstr "" +msgstr "Denne teksten er Kursiv" #: .\cookbook\templates\markdown_info.html:61 #: .\cookbook\templates\markdown_info.html:77 msgid "Blockquotes are also possible" -msgstr "" +msgstr "Det er også mulig å sitere avsnitt" #: .\cookbook\templates\markdown_info.html:84 msgid "Lists" -msgstr "" +msgstr "Lister" #: .\cookbook\templates\markdown_info.html:85 msgid "" @@ -1071,22 +1128,24 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:125 msgid "Images & Links" -msgstr "" +msgstr "Bilder og lenker" #: .\cookbook\templates\markdown_info.html:126 msgid "" "Links can be formatted with Markdown. This application also allows to paste " "links directly into markdown fields without any formatting." msgstr "" +"Lenker kan formateres med Markdown. Denne applikasjonen lar deg også lime " +"inn lenker direkte i Markdown-felt uten noen formatering." #: .\cookbook\templates\markdown_info.html:132 #: .\cookbook\templates\markdown_info.html:145 msgid "This will become an image" -msgstr "" +msgstr "Dette vil bli til et bilde" #: .\cookbook\templates\markdown_info.html:152 msgid "Tables" -msgstr "" +msgstr "Tabeller" #: .\cookbook\templates\markdown_info.html:153 msgid "" @@ -1094,124 +1153,130 @@ msgid "" "editor like this one." msgstr "" +"Markdown-tabeller er vanskelige å lage for hånd. Det anbefales å bruke en " +"tabellredigerer som denne." #: .\cookbook\templates\markdown_info.html:155 #: .\cookbook\templates\markdown_info.html:157 #: .\cookbook\templates\markdown_info.html:171 #: .\cookbook\templates\markdown_info.html:177 msgid "Table" -msgstr "" +msgstr "Tabell" #: .\cookbook\templates\markdown_info.html:155 #: .\cookbook\templates\markdown_info.html:172 msgid "Header" -msgstr "" +msgstr "Overskrift" #: .\cookbook\templates\markdown_info.html:157 #: .\cookbook\templates\markdown_info.html:178 msgid "Cell" -msgstr "" +msgstr "Celle" #: .\cookbook\templates\meal_plan.html:101 msgid "New Entry" -msgstr "" +msgstr "Ny oppføring" #: .\cookbook\templates\meal_plan.html:113 #: .\cookbook\templates\shopping_list.html:52 msgid "Search Recipe" -msgstr "" +msgstr "Søk oppskrift" #: .\cookbook\templates\meal_plan.html:139 msgid "Title" -msgstr "" +msgstr "Tittel" #: .\cookbook\templates\meal_plan.html:141 msgid "Note (optional)" -msgstr "" +msgstr "Merknad (valgfritt)" #: .\cookbook\templates\meal_plan.html:143 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" +"Du kan bruke Markdown for å formatere dette feltet. Se dokumentasjonen " +"her" #: .\cookbook\templates\meal_plan.html:147 #: .\cookbook\templates\meal_plan.html:251 msgid "Serving Count" -msgstr "" +msgstr "Antall porsjoner" #: .\cookbook\templates\meal_plan.html:153 msgid "Create only note" -msgstr "" +msgstr "Opprett kun en merknad" #: .\cookbook\templates\meal_plan.html:168 #: .\cookbook\templates\shopping_list.html:7 #: .\cookbook\templates\shopping_list.html:29 #: .\cookbook\templates\shopping_list.html:705 msgid "Shopping List" -msgstr "" +msgstr "Handleliste" #: .\cookbook\templates\meal_plan.html:172 msgid "Shopping list currently empty" -msgstr "" +msgstr "Handlelisten er for øyeblikket tom" #: .\cookbook\templates\meal_plan.html:175 msgid "Open Shopping List" -msgstr "" +msgstr "Åpne handlelisten" #: .\cookbook\templates\meal_plan.html:189 msgid "Plan" -msgstr "" +msgstr "Plan" #: .\cookbook\templates\meal_plan.html:196 msgid "Number of Days" -msgstr "" +msgstr "Antall dager" #: .\cookbook\templates\meal_plan.html:206 msgid "Weekday offset" -msgstr "" +msgstr "Ukedagsforskyvning" #: .\cookbook\templates\meal_plan.html:209 msgid "" "Number of days starting from the first day of the week to offset the default " "view." -msgstr "" +msgstr "Antall dager fra den første dagen i uken for å endre standardvisningen." #: .\cookbook\templates\meal_plan.html:217 #: .\cookbook\templates\meal_plan.html:294 msgid "Edit plan types" -msgstr "" +msgstr "Rediger plantyper" #: .\cookbook\templates\meal_plan.html:219 msgid "Show help" -msgstr "" +msgstr "Vis hjelp" #: .\cookbook\templates\meal_plan.html:220 msgid "Week iCal export" -msgstr "" +msgstr "Uke iCal-eksport" #: .\cookbook\templates\meal_plan.html:264 #: .\cookbook\templates\meal_plan_entry.html:18 msgid "Created by" -msgstr "" +msgstr "Opprettet av" #: .\cookbook\templates\meal_plan.html:270 #: .\cookbook\templates\meal_plan_entry.html:20 #: .\cookbook\templates\shopping_list.html:250 msgid "Shared with" -msgstr "" +msgstr "Delt med" #: .\cookbook\templates\meal_plan.html:280 msgid "Add to Shopping" -msgstr "" +msgstr "Legg til i handlelisten" #: .\cookbook\templates\meal_plan.html:323 msgid "New meal type" -msgstr "" +msgstr "Ny måltidstype" #: .\cookbook\templates\meal_plan.html:338 msgid "Meal Plan Help" -msgstr "" +msgstr "Hjelp for måltidsplanen" #: .\cookbook\templates\meal_plan.html:344 msgid "" @@ -1251,7 +1316,7 @@ msgstr "" #: .\cookbook\templates\meal_plan_entry.html:6 msgid "Meal Plan View" -msgstr "" +msgstr "Visning av måltidsplanen" #: .\cookbook\templates\meal_plan_entry.html:50 msgid "Never cooked before." @@ -1259,12 +1324,12 @@ msgstr "" #: .\cookbook\templates\meal_plan_entry.html:76 msgid "Other meals on this day" -msgstr "" +msgstr "Andre måltider denne dagen" #: .\cookbook\templates\no_groups_info.html:5 #: .\cookbook\templates\no_groups_info.html:12 msgid "No Permissions" -msgstr "Ingen tilganger." +msgstr "Ingen tilgang" #: .\cookbook\templates\no_groups_info.html:17 msgid "You do not have any groups and therefor cannot use this application." @@ -1298,12 +1363,11 @@ msgstr "" #: .\cookbook\templates\offline.html:6 msgid "Offline" -msgstr "Frakoblet." +msgstr "Frakoblet" #: .\cookbook\templates\offline.html:19 -#, fuzzy msgid "You are currently offline!" -msgstr "Du er ikke tilkoblet Internett." +msgstr "Du er ikke tilkoblet!" #: .\cookbook\templates\offline.html:20 msgid "" @@ -1366,7 +1430,7 @@ msgstr "Stil" #: .\cookbook\templates\settings.html:79 msgid "API Token" -msgstr "API-symbol" +msgstr "API nøkkel" #: .\cookbook\templates\settings.html:80 msgid "" @@ -1389,9 +1453,8 @@ msgid "Cookbook Setup" msgstr "Kokeboksoppsett" #: .\cookbook\templates\setup.html:14 -#, fuzzy msgid "Setup" -msgstr "Sett opp" +msgstr "Installering" #: .\cookbook\templates\setup.html:15 msgid "" @@ -1424,11 +1487,11 @@ msgstr "Mengde" #: .\cookbook\templates\shopping_list.html:226 msgid "Supermarket" -msgstr "Matbutikk" +msgstr "Butikk" #: .\cookbook\templates\shopping_list.html:236 msgid "Select Supermarket" -msgstr "Velg matbutikk" +msgstr "Velg butikk" #: .\cookbook\templates\shopping_list.html:260 msgid "Select User" @@ -1540,7 +1603,6 @@ msgstr "" #: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 -#, fuzzy msgid "Ok" msgstr "OK" diff --git a/cookbook/locale/nl/LC_MESSAGES/django.mo b/cookbook/locale/nl/LC_MESSAGES/django.mo index 055ff39e2..a316e1cac 100644 Binary files a/cookbook/locale/nl/LC_MESSAGES/django.mo and b/cookbook/locale/nl/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/nl/LC_MESSAGES/django.po b/cookbook/locale/nl/LC_MESSAGES/django.po index 1abdb96ea..0cb0f1227 100644 --- a/cookbook/locale/nl/LC_MESSAGES/django.po +++ b/cookbook/locale/nl/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" -"PO-Revision-Date: 2023-02-27 13:55+0000\n" -"Last-Translator: Jesse \n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" +"PO-Revision-Date: 2023-08-15 19:19+0000\n" +"Last-Translator: Jochum van der Heide \n" "Language-Team: Dutch \n" "Language: nl\n" @@ -64,7 +64,7 @@ msgstr "Ingrediënt decimalen" msgid "Shopping list auto sync period" msgstr "Boodschappenlijst auto sync periode" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "Opmerkingen" @@ -116,7 +116,7 @@ msgstr "Aantal decimalen om ingrediënten op af te ronden." msgid "If you want to be able to create and see comments underneath recipes." msgstr "Als je opmerkingen onder recepten wil kunnen maken en zien." -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -133,7 +133,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "Zet de navbar vast aan de bovenkant van de pagina." -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "Zet maaltijdplan ingrediënten automatisch op boodschappenlijst." @@ -153,11 +153,11 @@ msgstr "" "Beide velden zijn optioneel. Indien niks is opgegeven wordt de " "gebruikersnaam weergegeven" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "Naam" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "Etiketten" @@ -169,7 +169,7 @@ msgstr "Voorbereidingstijd in minuten" msgid "Waiting time (cooking/baking) in minutes" msgstr "Wacht tijd in minuten (koken en bakken)" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "Pad" @@ -177,11 +177,11 @@ msgstr "Pad" msgid "Storage UID" msgstr "Opslag UID" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "Standaard waarde" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." @@ -189,19 +189,19 @@ msgstr "" "Om dubbelingen te voorkomen worden recepten met dezelfde naam als een " "bestaand recept genegeerd. Vink aan om alles te importeren." -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "Voeg een opmerking toe: " -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "Laat leeg voor dropbox en vul het app wachtwoord in voor nextcloud." -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "Laat leeg voor nextcloud en vul de api token in voor dropbox." -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" @@ -209,33 +209,33 @@ msgstr "" "Laat leeg voor dropbox en vul enkel de base url voor nextcloud in. (/" "remote.php/webdav/ wordt automatisch toegevoegd.)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Opslag" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Actief" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Zoekopdracht" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "Bestands ID" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "Je moet minimaal één recept of titel te specificeren." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "Je kan in de instellingen standaard gebruikers in stellen om de recepten met " "te delen." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -243,15 +243,15 @@ msgstr "" "Je kunt markdown gebruiken om dit veld te op te maken. Bekijk de documentatie hier" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Maximum aantal gebruikers voor deze ruimte bereikt." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "E-mailadres reeds in gebruik!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -259,15 +259,15 @@ msgstr "" "Een e-mailadres is niet vereist, maar indien aanwezig zal de " "uitnodigingslink naar de gebruiker worden gestuurd." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Naam reeds in gebruik." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Accepteer voorwaarden" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -275,7 +275,7 @@ msgstr "" "Bepaalt hoe 'fuzzy' een zoekopdracht is als het trigram vergelijken gebruikt " "(lage waarden betekenen bijvoorbeeld dat meer typefouten genegeerd worden)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -283,7 +283,7 @@ msgstr "" "Selecteer zoekmethode. Klik hier voor een " "beschrijving van de keuzes." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -291,7 +291,7 @@ msgstr "" "Gebruik 'fuzzy' koppelen bij eenheden, etiketten en ingrediënten bij " "bewerken en importeren van recepten." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -300,7 +300,7 @@ msgstr "" "deze optie kan de zoekkwaliteit afhankelijk van de taal, zowel verbeteren " "als verslechteren" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" @@ -308,7 +308,7 @@ msgstr "" "Velden doorzoeken op gedeelde overeenkomsten. (zoeken op 'Appel' vindt " "'appel', 'aardappel' en 'appelsap')" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" @@ -316,7 +316,7 @@ msgstr "" "Velden doorzoeken op overeenkomsten aan het begin van het woord. (zoeken op " "'sa' vindt 'salade' en 'sandwich')" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -324,7 +324,7 @@ msgstr "" "Velden 'fuzzy' doorzoeken. (zoeken op 'recetp' vindt ook 'recept') Noot: " "deze optie conflicteert met de zoekmethoden 'web' en 'raw'." -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." @@ -332,35 +332,35 @@ msgstr "" "Velden doorzoeken op volledige tekst. Noot: Web, Zin en Raw zoekmethoden " "werken alleen met volledige tekstvelden." -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "Zoekmethode" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "'Fuzzy' zoekopdrachten" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "Negeer accent" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "Gedeeltelijke overeenkomst" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "Begint met" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "'Fuzzy' zoeken" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "Volledige tekst" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -368,7 +368,7 @@ msgstr "" "Gebruikers zien alle items die je op je boodschappenlijst zet. Ze moeten " "jou toevoegen om items op hun lijst te zien." -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." @@ -376,7 +376,7 @@ msgstr "" "Als een maaltijdplan aan de boodschappenlijst toegevoegd wordt (handmatig of " "automatisch), neem dan alle recepten op." -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." @@ -384,94 +384,94 @@ msgstr "" "Als een maaltijdplan aan de boodschappenlijst toegevoegd wordt (handmatig of " "automatisch), sluit ingrediënten die op voorraad zijn dan uit." -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "Standaard aantal uren om een boodschappenlijst item te vertragen." -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "Filter boodschappenlijst om alleen supermarktcategorieën te bevatten." -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "Dagen van recente boodschappenlijst items weer te geven." -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" "Markeer eten 'Op voorraad' wanneer het van het boodschappenlijstje is " "afgevinkt." -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "Scheidingsteken te gebruiken voor CSV exports." -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "" "Toe te voegen Voorvoegsel bij het kopiëren van een lijst naar het klembord." -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "Deel boodschappenlijst" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "Autosync" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "Voeg maaltijdplan automatisch toe" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "Sluit op voorraad uit" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "Neem gerelateerde op" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "Standaard vertraging in uren" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "Filter op supermarkt" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "Afgelopen dagen" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "CSV scheidingsteken" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "Lijst voorvoegsel" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "Auto op voorraad" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "Herstel Ingrediënt overname" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "Herstel alle ingrediënten om de geconfigureerde velden over te nemen." -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "Velden van ingrediënten die standaard overgenomen moeten worden." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Toon recepten teller bij zoekfilters" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "Gebruik de meervoudsvorm voor eenheden en voedsel in deze ruimte." @@ -484,7 +484,7 @@ msgstr "" "minuten en probeer het opnieuw." #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Je bent niet ingelogd en kan deze pagina daarom niet bekijken!" @@ -497,7 +497,7 @@ msgstr "Je bent niet ingelogd en kan deze pagina daarom niet bekijken!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Je hebt niet de benodigde machtigingen om deze pagina te bekijken!" @@ -517,11 +517,39 @@ msgstr "Je hebt het maximaal aantal recepten voor jouw ruimte bereikt." msgid "You have more users than allowed in your space." msgstr "Je hebt meer gebruikers dan toegestaan in jouw ruimte." -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "Er moet een queryset of hash_key opgegeven worden" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +msgid "reverse rotation" +msgstr "omgekeerde rotatie" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "voorzichtige rotatie" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "kneden" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "verdikken" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "opwarmen" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "gisten" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "sous-vide" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "Je moet een portiegrootte aanleveren" @@ -539,14 +567,14 @@ msgstr "Favoriet" msgid "I made this" msgstr "Ik heb dit gemaakt" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" "De importtool verwachtte een .zip bestand. Heb je het juiste type gekozen?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." @@ -554,24 +582,28 @@ msgstr "" "Er is een onverwachte fout opgetreden tijdens het importeren. Controleer of " "u een geldig bestand hebt geüpload." -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "De volgende recepten zijn genegeerd omdat ze al bestonden:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "%s recepten geïmporteerd." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" +msgstr "Bron van het recept:" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "Notities" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "Voedingswaarde" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "Bron" @@ -642,72 +674,72 @@ msgstr "" "Maximale bestandsopslag voor ruimte in MB. 0 voor onbeperkt, -1 om uploaden " "van bestanden uit te schakelen." -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Zoeken" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Maaltijdplan" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Boeken" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " is deel van een receptstap en kan niet verwijderd worden" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "Simpel" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "Zin" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "Web" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "Rauw" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "Ingrediënt alias" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "Eenheid alias" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "Etiket alias" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "Verrvang beschrijving" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "Vervang instructies" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Recept" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "Ingrediënt" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Etiket" @@ -723,48 +755,48 @@ msgstr "U heeft de uploadlimiet bereikt." msgid "Cannot modify Space owner permission." msgstr "Kan de rechten van de ruimte-eigenaar niet wijzigen." -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "Hallo" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "Je bent uitgenodigd door " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " om zijn/haar Tandoor Recepten ruimte " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "Klik om de volgende link om je account te activeren: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" "Als de linkt niet werkt, gebruik dan de volgende code om handmatig tot de " "ruimte toe te treden: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "De uitnodiging is geldig tot " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" "Tandoor Recepten is een Open Source recepten manager. Bekijk het op GitHub " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "Tandoor Recepten uitnodiging" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "Bestaande boodschappenlijst is bijgewerkt" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." @@ -772,22 +804,22 @@ msgstr "" "Lijst van ingrediënten ID's van het toe te voegen recept, als deze niet " "opgegeven worden worden alle ingrediënten toegevoegd." -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" "Als je een list_recipe ID en portiegrootte van 0 opgeeft wordt dat " "boodschappenlijstje verwijderd." -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "Hoeveelheid eten om aan het boodschappenlijstje toe te voegen" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "ID of eenheid om te gebruik voor het boodschappenlijstje" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" "Wanneer ingesteld op waar, wordt al het voedsel van actieve " @@ -1632,11 +1664,11 @@ msgstr "Terug" msgid "Profile" msgstr "Profiel" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "door" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "Opmerking" @@ -2275,85 +2307,85 @@ msgstr "" msgid "URL Import" msgstr "Importeer URL" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "Parameter updatet_at is onjuist geformateerd" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "Er bestaat geen {self.basename} met id {pk}" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "Kan niet met hetzelfde object samenvoegen!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "Er bestaat geen {self.basename} met id {target}" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "Kan niet met kindobject samenvoegen!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} is succesvol samengevoegd met {target.name}" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" "Er is een error opgetreden bij het samenvoegen van {source.name} met {target." "name}" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} is succesvol verplaatst naar het hoogste niveau." -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "Er is een error opgetreden bij het verplaatsen " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "Kan object niet verplaatsen naar zichzelf!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "Er bestaat geen {self.basename} met id {parent}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} is succesvol verplaatst naar {parent.name}" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} is verwijderd van het boodschappenlijstje." -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} is toegevoegd aan het boodschappenlijstje." -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" "ID van het recept waar de stap onderdeel van is. Herhaal parameter voor " "meerdere." -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "Zoekterm komt overeen (fuzzy) met object naam." -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." @@ -2361,7 +2393,7 @@ msgstr "" "Zoekterm komt overeen (fuzzy) met recept naam. In de toekomst wordt zoeken " "op volledige tekst ondersteund." -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" @@ -2369,109 +2401,109 @@ msgstr "" "ID van etiket dat een recept moet hebben. Herhaal parameter voor meerdere. " "Gelijkwaardig aan keywords_or" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" "Etiket ID, herhaal voor meerdere. Geeft recepten met elk geselecteerd etiket " "weer" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" "Etiket ID, herhaal voor meerdere. Geeft recepten met alle geselecteerde " "etiketten weer." -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" "Etiket ID, herhaal voor meerdere. Sluit recepten met één van de etiketten " "uit." -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" "Etiket ID, herhaal voor meerdere. Sluit recepten met alle etiketten uit." -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" "ID van ingrediënt dat een recept moet hebben. Herhaal parameter voor " "meerdere." -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" "Ingrediënt ID, herhaal voor meerdere. Geeft recepten met elk ingrediënt weer" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" "Ingrediënt ID, herhaal voor meerdere. Geef recepten met alle ingrediënten " "weer." -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" "Ingrediënt ID, herhaal voor meerdere. sluit recepten met één van de " "ingrediënten uit." -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" "Ingrediënt ID, herhaal voor meerdere. Sluit recepten met alle ingrediënten " "uit." -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "ID van eenheid dat een recept moet hebben." -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "Een waardering van een recept gaat van 0 tot 5." -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" "ID van boek dat een recept moet hebben. Herhaal parameter voor meerdere." -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "Boek ID, herhaal voor meerdere. Geeft recepten uit alle boeken weer" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "Boek IDs, herhaal voor meerdere. Geeft recepten weer uit alle boeken." -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" "Boek IDs, herhaal voor meerdere. Sluit recepten uit elk van de boeken uit." -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "Boek IDs, herhaal voor meerdere. Sluit recepten uit alle boeken uit." -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" "Wanneer alleen interne recepten gevonden moeten worden. [waar/onwaar]" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" "Geeft de resultaten in willekeurige volgorde weer. [waar/onwaar]" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "Geeft nieuwe resultaten eerst weer. [waar/onwaar]" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" @@ -2479,7 +2511,7 @@ msgstr "" "Filter recepten X maal of meer bereid. Negatieve waarden geven minder dan X " "keer bereide recepten weer" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." @@ -2487,7 +2519,7 @@ msgstr "" "Filter recepten op laatst bereid op of na JJJJ-MM-DD. Voorafgaand - filters " "op of voor datum." -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." @@ -2495,7 +2527,7 @@ msgstr "" "Filter recepten aangemaakt op of na JJJJ-MM-DD. Voorafgaand - filters op of " "voor datum." -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." @@ -2503,7 +2535,7 @@ msgstr "" "Filter recepten op geüpdatet op of na JJJJ-MM-DD. Voorafgaand - filters op " "of voor datum." -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." @@ -2511,13 +2543,13 @@ msgstr "" "Filter recepten op laatst bekeken op of na JJJJ-MM-DD. Voorafgaand - filters " "op of voor datum." -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" "Filter recepten die bereid kunnen worden met ingrediënten die op voorraad " "zijn. [waar/onwaar]" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." @@ -2525,7 +2557,7 @@ msgstr "" "Geeft het boodschappenlijstje item met een primaire sleutel van id. " "Meerdere waarden toegestaan." -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." @@ -2533,45 +2565,45 @@ msgstr "" "Filter boodschappenlijstjes op aangevinkt. [waar,onwaar,beide,recent]
    - recent bevat niet aangevinkte en recent voltooide items." -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" "Geeft items op boodschappenlijstjes gesorteerd per supermarktcategorie weer." -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "Niks te doen." -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "Ongeldige URL" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "Verbinding geweigerd." -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "Verkeerd URL schema." -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "Er is geen bruikbare data gevonden." -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "Importeren is voor deze provider niet geïmplementeerd" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "Deze optie is nog niet beschikbaar in de gehoste versie van Tandoor!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "Synchronisatie succesvol!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "Er is een fout opgetreden bij het synchroniseren met Opslag" @@ -2634,7 +2666,7 @@ msgstr "Wijzigingen opgeslagen!" msgid "Error saving changes!" msgstr "Fout bij het opslaan van de wijzigingen!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2682,7 +2714,12 @@ msgstr "Nieuw recept geïmporteerd!" msgid "There was an error importing this recipe!" msgstr "Er is een fout opgetreden bij het importeren van dit recept!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "Deze optie is niet beschikbaar in de demo versie!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." @@ -2690,24 +2727,19 @@ msgstr "" "Je hebt je eigen recepten ruimte succesvol aangemaakt. Start met het " "toevoegen van recepten of nodig anderen uit om je te vergezellen." -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "Je beschikt niet over de juiste rechten om deze actie uit te voeren!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "Opmerking opgeslagen!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "Deze optie is niet beschikbaar in de demo versie!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "Je moet tenminste één veld om te doorzoeken selecteren!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" @@ -2715,11 +2747,11 @@ msgstr "" "Om deze zoekmethode te gebruiken moet je tenminste één volledig tekstveld " "selecteren!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "'Fuzzy' zoeken is niet te gebruiken met deze zoekmethode!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2730,27 +2762,27 @@ msgstr "" "documentatie raad moeten plegen voor een methode om je wachtwoord te " "resetten." -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "Wachtwoorden komen niet overeen!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "Gebruiker is gecreëerd, Log in alstublieft!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "Onjuiste uitnodigingslink opgegeven!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "Succesvol toegetreden tot ruimte." -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "De uitnodigingslink is niet valide of al gebruikt!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." @@ -2758,7 +2790,7 @@ msgstr "" "Het rapporteren van gedeelde links is niet geactiveerd voor deze instantie. " "Rapporteer problemen bij de beheerder van de pagina." -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/pt/LC_MESSAGES/django.po b/cookbook/locale/pt/LC_MESSAGES/django.po index 35e120009..192d465c1 100644 --- a/cookbook/locale/pt/LC_MESSAGES/django.po +++ b/cookbook/locale/pt/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" "PO-Revision-Date: 2023-01-08 17:55+0000\n" "Last-Translator: Joachim Weber \n" "Language-Team: Portuguese /remote." "php/webdav/ is added automatically)" @@ -209,33 +209,33 @@ msgstr "" "Deixar vazio para Dropbox e inserir apenas url base para Nextcloud (/" "remote.php/webdav/é adicionado automaticamente). " -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "Armazenamento" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Ativo" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "Procurar" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "ID the ficheiro" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "É necessário inserir uma receita ou um título." -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" "É possível escolher os utilizadores com quem partilhar receitas por defeitos " "nas definições." -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" @@ -243,15 +243,15 @@ msgstr "" "É possível utilizar markdown para editar este campo. Documentação disponível aqui" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "Número máximo de utilizadores alcançado." -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "Endereço email já utilizado!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." @@ -259,15 +259,15 @@ msgstr "" "Um endereço de email não é obrigatório mas se fornecido será enviada uma " "mensagem ao utilizador." -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "Nome já existente." -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "Aceitar Termos e Condições" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -276,7 +276,7 @@ msgstr "" "de semelhança de trigrama (valores mais baixos significam que mais erros são " "ignorados)." -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 #, fuzzy #| msgid "" #| "Select type method of search. Click here " @@ -288,7 +288,7 @@ msgstr "" "Selecionar o método de pesquisa. Uma descrição completa das opções pode ser " "encontrada aqui." -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." @@ -296,7 +296,7 @@ msgstr "" "Utilizar correspondência difusa em unidades, palavras-chave e ingredientes " "ao editar e importar receitas." -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" @@ -304,171 +304,171 @@ msgstr "" "Campos de pesquisa que ignoram pontuação. Esta opção pode aumentar ou " "diminuir a qualidade de pesquisa dependendo da língua em uso" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 #, fuzzy #| msgid "Search" msgid "Search Method" msgstr "Procurar" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 #, fuzzy #| msgid "Search" msgid "Fuzzy Search" msgstr "Procurar" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 #, fuzzy #| msgid "Text" msgid "Full Text" msgstr "Texto" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 #, fuzzy #| msgid "Shopping" msgid "Share Shopping List" msgstr "Compras" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 #, fuzzy #| msgid "Food that should be replaced." msgid "Fields on food that should be inherited by default." msgstr "Prato a ser alterado." -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "Mostrar receitas recentes na página de pesquisa" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -479,7 +479,7 @@ msgid "" msgstr "" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "Autenticação necessária para aceder a esta página!" @@ -492,7 +492,7 @@ msgstr "Autenticação necessária para aceder a esta página!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "Sem permissões para aceder a esta página!" @@ -511,11 +511,41 @@ msgstr "" msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "Use fractions" +msgid "reverse rotation" +msgstr "Usar frações" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "É necessário inserir uma receita ou um título" @@ -533,38 +563,44 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "%s receitas importadas." -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +#, fuzzy +#| msgid "Recipes" +msgid "Recipe source:" +msgstr "Receitas" + +#: .\cookbook\integration\paprika.py:49 #, fuzzy #| msgid "Note" msgid "Notes" msgstr "Nota" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "" @@ -633,82 +669,82 @@ msgid "" "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "Procurar" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "Plano de refeição" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "Livros" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr "" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "New Food" msgid "Food Alias" msgstr "Novo Prato" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Units" msgid "Unit Alias" msgstr "Unidades" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 #, fuzzy #| msgid "Keywords" msgid "Keyword Alias" msgstr "Palavras-chave" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 #, fuzzy #| msgid "Instructions" msgid "Instruction Replace" msgstr "Instruções" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "Receita" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 #, fuzzy #| msgid "New Food" msgid "Food" msgstr "Novo Prato" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "Palavra-chave" @@ -724,64 +760,64 @@ msgstr "" msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "" -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr "" -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "" -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "" -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1614,11 +1650,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "por" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "" @@ -2108,258 +2144,258 @@ msgstr "" msgid "URL Import" msgstr "" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "" -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "" @@ -2420,7 +2456,7 @@ msgstr "" msgid "Error saving changes!" msgstr "" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2468,73 +2504,73 @@ msgstr "" msgid "There was an error importing this recipe!" msgstr "" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " "on how to reset passwords." msgstr "" -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/pt_BR/LC_MESSAGES/django.mo b/cookbook/locale/pt_BR/LC_MESSAGES/django.mo index fd3ed3ef5..181b78e72 100644 Binary files a/cookbook/locale/pt_BR/LC_MESSAGES/django.mo and b/cookbook/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/pt_BR/LC_MESSAGES/django.po b/cookbook/locale/pt_BR/LC_MESSAGES/django.po index 2c96227d7..c3eff99e4 100644 --- a/cookbook/locale/pt_BR/LC_MESSAGES/django.po +++ b/cookbook/locale/pt_BR/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-02-11 08:52+0100\n" -"PO-Revision-Date: 2023-02-18 10:55+0000\n" -"Last-Translator: Joachim Weber \n" +"PO-Revision-Date: 2023-04-12 11:55+0000\n" +"Last-Translator: noxonad \n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" @@ -2208,7 +2208,7 @@ msgstr "" #: .\cookbook\templates\url_import.html:38 msgid "URL" -msgstr "" +msgstr "URL" #: .\cookbook\templates\url_import.html:40 msgid "App" diff --git a/cookbook/locale/rn/LC_MESSAGES/django.po b/cookbook/locale/rn/LC_MESSAGES/django.po index 67073611a..ad65d22b3 100644 --- a/cookbook/locale/rn/LC_MESSAGES/django.po +++ b/cookbook/locale/rn/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -58,7 +58,7 @@ msgstr "" msgid "Shopping list auto sync period" msgstr "" -#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:21 +#: .\cookbook\forms.py:62 .\cookbook\templates\recipe_view.html:36 msgid "Comments" msgstr "" @@ -102,7 +102,7 @@ msgstr "" msgid "If you want to be able to create and see comments underneath recipes." msgstr "" -#: .\cookbook\forms.py:79 .\cookbook\forms.py:491 +#: .\cookbook\forms.py:79 .\cookbook\forms.py:509 msgid "" "Setting to 0 will disable auto sync. When viewing a shopping list the list " "is updated every set seconds to sync changes someone else might have made. " @@ -114,7 +114,7 @@ msgstr "" msgid "Makes the navbar stick to the top of the page." msgstr "" -#: .\cookbook\forms.py:83 .\cookbook\forms.py:494 +#: .\cookbook\forms.py:83 .\cookbook\forms.py:512 msgid "Automatically add meal plan ingredients to shopping list." msgstr "" @@ -132,11 +132,11 @@ msgid "" "instead" msgstr "" -#: .\cookbook\forms.py:123 .\cookbook\forms.py:296 +#: .\cookbook\forms.py:123 .\cookbook\forms.py:314 msgid "Name" msgstr "" -#: .\cookbook\forms.py:124 .\cookbook\forms.py:297 .\cookbook\views\lists.py:88 +#: .\cookbook\forms.py:124 .\cookbook\forms.py:315 .\cookbook\views\lists.py:88 msgid "Keywords" msgstr "" @@ -148,7 +148,7 @@ msgstr "" msgid "Waiting time (cooking/baking) in minutes" msgstr "" -#: .\cookbook\forms.py:127 .\cookbook\forms.py:265 .\cookbook\forms.py:298 +#: .\cookbook\forms.py:127 .\cookbook\forms.py:283 .\cookbook\forms.py:316 msgid "Path" msgstr "" @@ -156,265 +156,265 @@ msgstr "" msgid "Storage UID" msgstr "" -#: .\cookbook\forms.py:160 +#: .\cookbook\forms.py:161 msgid "Default" msgstr "" -#: .\cookbook\forms.py:172 +#: .\cookbook\forms.py:190 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." msgstr "" -#: .\cookbook\forms.py:195 +#: .\cookbook\forms.py:213 msgid "Add your comment: " msgstr "" -#: .\cookbook\forms.py:210 +#: .\cookbook\forms.py:228 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" -#: .\cookbook\forms.py:217 +#: .\cookbook\forms.py:235 msgid "Leave empty for nextcloud and enter api token for dropbox." msgstr "" -#: .\cookbook\forms.py:226 +#: .\cookbook\forms.py:244 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" msgstr "" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "" -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "" -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "" -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "" -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -425,7 +425,7 @@ msgid "" msgstr "" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "" @@ -438,7 +438,7 @@ msgstr "" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "" @@ -457,11 +457,39 @@ msgstr "" msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +msgid "reverse rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "" @@ -479,36 +507,40 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "" -#: .\cookbook\integration\paprika.py:46 -msgid "Notes" +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" msgstr "" #: .\cookbook\integration\paprika.py:49 +msgid "Notes" +msgstr "" + +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "" @@ -575,72 +607,72 @@ msgid "" "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr "" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "" @@ -656,64 +688,64 @@ msgstr "" msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "" -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr "" -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "" -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "" -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1501,11 +1533,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "" @@ -1983,258 +2015,258 @@ msgstr "" msgid "URL Import" msgstr "" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "" -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "" @@ -2295,7 +2327,7 @@ msgstr "" msgid "Error saving changes!" msgstr "" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2341,73 +2373,73 @@ msgstr "" msgid "There was an error importing this recipe!" msgstr "" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " "on how to reset passwords." msgstr "" -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/ro/LC_MESSAGES/django.mo b/cookbook/locale/ro/LC_MESSAGES/django.mo index acb36632e..980a56264 100644 Binary files a/cookbook/locale/ro/LC_MESSAGES/django.mo and b/cookbook/locale/ro/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/ro/LC_MESSAGES/django.po b/cookbook/locale/ro/LC_MESSAGES/django.po index 7cd2aecef..f8867bdab 100644 --- a/cookbook/locale/ro/LC_MESSAGES/django.po +++ b/cookbook/locale/ro/LC_MESSAGES/django.po @@ -8,113 +8,123 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-08 16:27+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2023-04-27 08:55+0000\n" +"Last-Translator: noxonad \n" +"Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125 #: .\cookbook\templates\forms\ingredients.html:34 #: .\cookbook\templates\space.html:43 .\cookbook\templates\stats.html:28 #: .\cookbook\templates\url_import.html:274 msgid "Ingredients" -msgstr "" +msgstr "Ingrediente" #: .\cookbook\forms.py:54 msgid "Default unit" -msgstr "" +msgstr "Unitate implicită" #: .\cookbook\forms.py:55 msgid "Use fractions" -msgstr "" +msgstr "Utilizare fracții" #: .\cookbook\forms.py:56 msgid "Use KJ" -msgstr "" +msgstr "Utilizare KJ" #: .\cookbook\forms.py:57 msgid "Theme" -msgstr "" +msgstr "Teme" #: .\cookbook\forms.py:58 msgid "Navbar color" -msgstr "" +msgstr "Culoarea barei de navigare" #: .\cookbook\forms.py:59 msgid "Sticky navbar" -msgstr "" +msgstr "Bară de navigare lipicioasă" #: .\cookbook\forms.py:60 msgid "Default page" -msgstr "" +msgstr "Pagină implicită" #: .\cookbook\forms.py:61 msgid "Show recent recipes" -msgstr "" +msgstr "Afișează rețete recente" #: .\cookbook\forms.py:62 msgid "Search style" -msgstr "" +msgstr "Căutare stil" #: .\cookbook\forms.py:63 msgid "Plan sharing" -msgstr "" +msgstr "Partajarea planurilor" #: .\cookbook\forms.py:64 msgid "Ingredient decimal places" -msgstr "" +msgstr "Zecimale ale ingredientelor" #: .\cookbook\forms.py:65 msgid "Shopping list auto sync period" -msgstr "" +msgstr "Perioada de sincronizare automată a listei de cumpărături" #: .\cookbook\forms.py:66 .\cookbook\templates\recipe_view.html:21 #: .\cookbook\templates\space.html:62 .\cookbook\templates\stats.html:47 msgid "Comments" -msgstr "" +msgstr "Comentarii" #: .\cookbook\forms.py:70 msgid "" "Color of the top navigation bar. Not all colors work with all themes, just " "try them out!" msgstr "" +"Culoarea barei de navigare de sus. Nu toate culorile funcționează cu toate " +"temele, doar încercați-le!" #: .\cookbook\forms.py:72 msgid "Default Unit to be used when inserting a new ingredient into a recipe." msgstr "" +"Unitate implicită care trebuie utilizată la introducerea unui ingredient nou " +"într-o rețetă." #: .\cookbook\forms.py:74 msgid "" "Enables support for fractions in ingredient amounts (e.g. convert decimals " "to fractions automatically)" msgstr "" +"Permite suport pentru fracții în cantități de ingrediente (de exemplu, " +"conversia zecimalelor în fracții automat)" #: .\cookbook\forms.py:76 msgid "Display nutritional energy amounts in joules instead of calories" -msgstr "" +msgstr "Afișați cantitățile de energie nutrițională în Jouli în loc de calorii" #: .\cookbook\forms.py:78 msgid "" "Users with whom newly created meal plan/shopping list entries should be " "shared by default." msgstr "" +"Utilizatorii cu care intrările din planul alimentar/lista de cumpărături nou " +"create ar trebui să fie partajate în mod implicit." #: .\cookbook\forms.py:80 msgid "Show recently viewed recipes on search page." -msgstr "" +msgstr "Afișați rețetele vizualizate recent pe pagina de căutare." #: .\cookbook\forms.py:81 msgid "Number of decimals to round ingredients." -msgstr "" +msgstr "Numărul de zecimale la ingrediente rotunde." #: .\cookbook\forms.py:82 msgid "If you want to be able to create and see comments underneath recipes." -msgstr "" +msgstr "Dacă doriți să puteți crea și vedea comentarii sub rețete." #: .\cookbook\forms.py:84 msgid "" @@ -123,211 +133,250 @@ msgid "" "Useful when shopping with multiple people but might use a little bit of " "mobile data. If lower than instance limit it is reset when saving." msgstr "" +"Setarea la 0 va dezactiva sincronizarea automată. Atunci când vizualizați o " +"listă de cumpărături, lista este actualizată la fiecare câteva secunde " +"setate pentru a sincroniza modificările pe care altcineva le-ar fi putut " +"face. Util atunci când faceți cumpărături cu mai multe persoane, dar ar " +"putea folosi un pic de date mobile. Dacă este mai mică decât limita " +"instanței, aceasta este resetată la salvare." #: .\cookbook\forms.py:87 msgid "Makes the navbar stick to the top of the page." -msgstr "" +msgstr "Face ca bara de navigare să se lipească în partea de sus a paginii." #: .\cookbook\forms.py:103 msgid "" "Both fields are optional. If none are given the username will be displayed " "instead" msgstr "" +"Ambele câmpuri sunt opționale. Dacă niciuna nu este setată, numele de " +"utilizator va fi afișată în schimb" #: .\cookbook\forms.py:124 .\cookbook\forms.py:289 #: .\cookbook\templates\url_import.html:158 msgid "Name" -msgstr "" +msgstr "Nume" #: .\cookbook\forms.py:125 .\cookbook\forms.py:290 #: .\cookbook\templates\space.html:39 .\cookbook\templates\stats.html:24 #: .\cookbook\templates\url_import.html:192 #: .\cookbook\templates\url_import.html:578 .\cookbook\views\lists.py:112 msgid "Keywords" -msgstr "" +msgstr "Cuvinte cheie" #: .\cookbook\forms.py:126 msgid "Preparation time in minutes" -msgstr "" +msgstr "Timp de pregătire în minute" #: .\cookbook\forms.py:127 msgid "Waiting time (cooking/baking) in minutes" -msgstr "" +msgstr "Timp de așteptare (gătit/coacere) în minute" #: .\cookbook\forms.py:128 .\cookbook\forms.py:259 .\cookbook\forms.py:291 msgid "Path" -msgstr "" +msgstr "Drum" #: .\cookbook\forms.py:129 msgid "Storage UID" -msgstr "" +msgstr "UID de stocare" #: .\cookbook\forms.py:157 msgid "Default" -msgstr "" +msgstr "Standard" #: .\cookbook\forms.py:168 .\cookbook\templates\url_import.html:94 msgid "" "To prevent duplicates recipes with the same name as existing ones are " "ignored. Check this box to import everything." msgstr "" +"Pentru a preveni duplicatele, rețetele cu același nume ca și cele existente " +"sunt ignorate. Bifați această casetă pentru a importa totul." #: .\cookbook\forms.py:190 msgid "Add your comment: " -msgstr "" +msgstr "Adaugă comentariul tău: " #: .\cookbook\forms.py:205 msgid "Leave empty for dropbox and enter app password for nextcloud." msgstr "" +"Lăsați gol pentru dropbox și introduceți parola aplicației pentru nextcloud." #: .\cookbook\forms.py:212 msgid "Leave empty for nextcloud and enter api token for dropbox." -msgstr "" +msgstr "Lăsați gol pentru nextcloud și introduceți token-ul API pentru dropbox." #: .\cookbook\forms.py:221 msgid "" "Leave empty for dropbox and enter only base url for nextcloud (/remote." "php/webdav/ is added automatically)" msgstr "" +"Lăsați gol pentru dropbox și introduceți numai URL-ul de bază pentru " +"nextcloud (/remote.php/webdav/ este adăugat automat)" #: .\cookbook\forms.py:258 .\cookbook\views\edit.py:166 msgid "Storage" -msgstr "" +msgstr "Stocare" #: .\cookbook\forms.py:260 msgid "Active" -msgstr "" +msgstr "Activ" #: .\cookbook\forms.py:265 msgid "Search String" -msgstr "" +msgstr "Șir de căutare" #: .\cookbook\forms.py:292 msgid "File ID" -msgstr "" +msgstr "ID-ul fișierului" #: .\cookbook\forms.py:313 msgid "You must provide at least a recipe or a title." -msgstr "" +msgstr "Trebuie să furnizați cel puțin o rețetă sau un titlu." #: .\cookbook\forms.py:326 msgid "You can list default users to share recipes with in the settings." msgstr "" +"Puteți lista utilizatorii impliciți cu care să partajați rețete în setări." #: .\cookbook\forms.py:327 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" +"Puteți utiliza markdown pentru a formata acest câmp. Vezi documentația aici" #: .\cookbook\forms.py:353 msgid "Maximum number of users for this space reached." -msgstr "" +msgstr "Numărul maxim de utilizatori pentru acest spațiu atins." #: .\cookbook\forms.py:359 msgid "Email address already taken!" -msgstr "" +msgstr "Adresa de e-mail deja în uz!" #: .\cookbook\forms.py:367 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" +"Nu este necesară o adresă de e-mail, dar dacă este prezentă, linkul de " +"invitație va fi trimis utilizatorului." #: .\cookbook\forms.py:382 msgid "Name already taken." -msgstr "" +msgstr "Nume deja în uz." #: .\cookbook\forms.py:393 msgid "Accept Terms and Privacy" -msgstr "" +msgstr "Acceptă condițiile și politicile de confidențialitate" #: .\cookbook\forms.py:425 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" +"Determină cât de vagă este o căutare dacă utilizează potrivirea " +"similitudinii trigramelor (de exemplu, valorile scăzute înseamnă că mai " +"multe greșeli de scriere sunt ignorate)." #: .\cookbook\forms.py:435 msgid "" "Select type method of search. Click here for " "full desciption of choices." msgstr "" +"Selectează metoda de căutare. Apasă aici " +"pentru descrierea completă a alegerilor." #: .\cookbook\forms.py:436 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" +"Utilizați potrivirea vagă pe unități, cuvinte cheie și ingrediente atunci " +"când editați și importați rețete." #: .\cookbook\forms.py:438 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" +"Câmpuri pentru a căuta ignorând accente. Selectarea acestei opțiuni poate " +"îmbunătăți sau degrada calitatea căutării în funcție de limbă" #: .\cookbook\forms.py:440 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" +"Câmpuri pentru a căuta potriviri parțiale. (de exemplu, căutarea " +"'Plăcintei' va returna 'plăcintă' și 'plăci' și 'simplă')" #: .\cookbook\forms.py:442 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" +"Câmpuri pentru a căuta începutul potrivirilor de cuvinte. (ex. căutarea 'sa' " +"va returna 'salată' și 'sandwich')" #: .\cookbook\forms.py:444 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" +"Câmpuri pentru căutarea 'vagă'. (ex., căutarea 'rețetă' va găsi 'rețetă'. " +"Notă: această opțiune va intra în conflict cu metodele de căutare 'web' și " +"'raw'." #: .\cookbook\forms.py:446 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" +"Câmpuri pentru căutarea completă a textului. Notă: metodele de căutare " +"'web', 'frază' și 'raw' funcționează numai cu câmpuri full-text." #: .\cookbook\forms.py:450 msgid "Search Method" -msgstr "" +msgstr "Metodă de căutare" #: .\cookbook\forms.py:451 msgid "Fuzzy Lookups" -msgstr "" +msgstr "Căutare vagă" #: .\cookbook\forms.py:452 msgid "Ignore Accent" -msgstr "" +msgstr "Ignoră accent" #: .\cookbook\forms.py:453 msgid "Partial Match" -msgstr "" +msgstr "Potrivire parțială" #: .\cookbook\forms.py:454 msgid "Starts Wtih" -msgstr "" +msgstr "Începe cu" #: .\cookbook\forms.py:455 msgid "Fuzzy Search" -msgstr "" +msgstr "Căutare vagă" #: .\cookbook\forms.py:456 msgid "Full Text" -msgstr "" +msgstr "Text complet" #: .\cookbook\helper\AllAuthCustomAdapter.py:36 msgid "" "In order to prevent spam, the requested email was not send. Please wait a " "few minutes and try again." msgstr "" +"Pentru a preveni spam-ul, e-mailul solicitat nu a fost trimis. Vă rugăm să " +"așteptați câteva minute și încercați din nou." #: .\cookbook\helper\permission_helper.py:136 #: .\cookbook\helper\permission_helper.py:159 .\cookbook\views\views.py:149 msgid "You are not logged in and therefore cannot view this page!" msgstr "" +"Nu sunteți conectat și, prin urmare, nu puteți vizualiza această pagină!" #: .\cookbook\helper\permission_helper.py:140 #: .\cookbook\helper\permission_helper.py:146 @@ -339,18 +388,18 @@ msgstr "" #: .\cookbook\views\views.py:160 .\cookbook\views\views.py:167 #: .\cookbook\views\views.py:233 msgid "You do not have the required permissions to view this page!" -msgstr "" +msgstr "Nu aveți permisiunile necesare pentru a vizualiza această pagină!" #: .\cookbook\helper\permission_helper.py:164 #: .\cookbook\helper\permission_helper.py:187 #: .\cookbook\helper\permission_helper.py:202 msgid "You cannot interact with this object as it is not owned by you!" -msgstr "" +msgstr "Nu poți interacționa cu acest obiect, deoarece nu este deținut de tine!" #: .\cookbook\helper\template_helper.py:61 #: .\cookbook\helper\template_helper.py:63 msgid "Could not parse template code." -msgstr "" +msgstr "Nu s-a putut analiza codul șablonului." #: .\cookbook\integration\integration.py:126 #: .\cookbook\templates\import.html:14 .\cookbook\templates\import.html:20 @@ -363,191 +412,199 @@ msgstr "" #: .\cookbook\templates\url_import.html:609 .\cookbook\views\delete.py:89 #: .\cookbook\views\edit.py:200 msgid "Import" -msgstr "" +msgstr "Importă" #: .\cookbook\integration\integration.py:208 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" +"Importatorul se aștepta la un fișier.zip. Ați ales tipul corect de " +"importator pentru datele dvs.?" #: .\cookbook\integration\integration.py:211 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" +"A apărut o eroare neașteptată în timpul importului. Asigurați-vă că ați " +"încărcat un fișier valid." #: .\cookbook\integration\integration.py:216 msgid "The following recipes were ignored because they already existed:" -msgstr "" +msgstr "Următoarele rețete au fost ignorate pentru că existau deja:" #: .\cookbook\integration\integration.py:220 #, python-format msgid "Imported %s recipes." -msgstr "" +msgstr "%s rețete importate." #: .\cookbook\integration\paprika.py:46 msgid "Notes" -msgstr "" +msgstr "Note" #: .\cookbook\integration\paprika.py:49 msgid "Nutritional Information" -msgstr "" +msgstr "Informații nutriționale" #: .\cookbook\integration\paprika.py:53 .\cookbook\templates\url_import.html:40 msgid "Source" -msgstr "" +msgstr "Sursă" #: .\cookbook\integration\safron.py:23 #: .\cookbook\templates\include\log_cooking.html:16 #: .\cookbook\templates\url_import.html:228 #: .\cookbook\templates\url_import.html:459 msgid "Servings" -msgstr "" +msgstr "Porții" #: .\cookbook\integration\safron.py:25 msgid "Waiting time" -msgstr "" +msgstr "Timp de așteptare" #: .\cookbook\integration\safron.py:27 msgid "Preparation Time" -msgstr "" +msgstr "Timp de pregătire" #: .\cookbook\integration\safron.py:29 .\cookbook\templates\base.html:78 #: .\cookbook\templates\forms\ingredients.html:7 #: .\cookbook\templates\index.html:7 msgid "Cookbook" -msgstr "" +msgstr "Bucate" #: .\cookbook\integration\safron.py:31 msgid "Section" -msgstr "" +msgstr "Secțiune" #: .\cookbook\management\commands\rebuildindex.py:14 msgid "Rebuilds full text search index on Recipe" -msgstr "" +msgstr "Reconstruiește indexul de căutare text complet pe rețetă" #: .\cookbook\management\commands\rebuildindex.py:18 msgid "Only Postgress databases use full text search, no index to rebuild" msgstr "" +"Numai bazele de date Postgress utilizează căutare text complet, nici un " +"index de reconstruit" #: .\cookbook\management\commands\rebuildindex.py:29 msgid "Recipe index rebuild complete." -msgstr "" +msgstr "Index rețetă reconstruit complet." #: .\cookbook\management\commands\rebuildindex.py:31 msgid "Recipe index rebuild failed." -msgstr "" +msgstr "Reconstruirea index-ului rețetă nu a reușit." #: .\cookbook\migrations\0047_auto_20200602_1133.py:14 msgid "Breakfast" -msgstr "" +msgstr "Mic dejun" #: .\cookbook\migrations\0047_auto_20200602_1133.py:19 msgid "Lunch" -msgstr "" +msgstr "Prânz" #: .\cookbook\migrations\0047_auto_20200602_1133.py:24 msgid "Dinner" -msgstr "" +msgstr "Cină" #: .\cookbook\migrations\0047_auto_20200602_1133.py:29 msgid "Other" -msgstr "" +msgstr "Altele" #: .\cookbook\models.py:150 msgid "" "Maximum file storage for space in MB. 0 for unlimited, -1 to disable file " "upload." msgstr "" +"Spațiu maxim de stocare a fișierelor pentru spațiu în MB. 0 pentru " +"nelimitat, -1 pentru a dezactiva încărcarea fișierelor." #: .\cookbook\models.py:202 .\cookbook\templates\search.html:7 #: .\cookbook\templates\shopping_list.html:59 msgid "Search" -msgstr "" +msgstr "Căutare" #: .\cookbook\models.py:203 .\cookbook\templates\base.html:82 #: .\cookbook\templates\meal_plan.html:7 #: .\cookbook\templates\meal_plan_new.html:7 .\cookbook\views\delete.py:181 #: .\cookbook\views\edit.py:220 .\cookbook\views\new.py:184 msgid "Meal-Plan" -msgstr "" +msgstr "Plan de alimentare" #: .\cookbook\models.py:204 .\cookbook\templates\base.html:90 msgid "Books" -msgstr "" +msgstr "Cărți" #: .\cookbook\models.py:212 msgid "Small" -msgstr "" +msgstr "Mic" #: .\cookbook\models.py:212 msgid "Large" -msgstr "" +msgstr "Mare" #: .\cookbook\models.py:212 .\cookbook\templates\generic\new_template.html:6 #: .\cookbook\templates\generic\new_template.html:14 msgid "New" -msgstr "" +msgstr "Nou" #: .\cookbook\models.py:396 msgid " is part of a recipe step and cannot be deleted" -msgstr "" +msgstr " face parte dintr-un pas de rețetă și nu poate fi șters" #: .\cookbook\models.py:441 .\cookbook\templates\url_import.html:42 msgid "Text" -msgstr "" +msgstr "Text" #: .\cookbook\models.py:441 msgid "Time" -msgstr "" +msgstr "Timp" #: .\cookbook\models.py:441 .\cookbook\templates\url_import.html:44 msgid "File" -msgstr "" +msgstr "Fișier" #: .\cookbook\models.py:441 #: .\cookbook\templates\include\recipe_open_modal.html:7 #: .\cookbook\views\delete.py:39 .\cookbook\views\edit.py:260 #: .\cookbook\views\new.py:53 msgid "Recipe" -msgstr "" +msgstr "Rețetă" #: .\cookbook\models.py:871 .\cookbook\templates\search_info.html:28 msgid "Simple" -msgstr "" +msgstr "Simplu" #: .\cookbook\models.py:872 .\cookbook\templates\search_info.html:33 msgid "Phrase" -msgstr "" +msgstr "Frază" #: .\cookbook\models.py:873 .\cookbook\templates\search_info.html:38 msgid "Web" -msgstr "" +msgstr "Web" #: .\cookbook\models.py:874 .\cookbook\templates\search_info.html:47 msgid "Raw" -msgstr "" +msgstr "Crud" #: .\cookbook\models.py:912 msgid "Food Alias" -msgstr "" +msgstr "Pseudonim produse alimentare" #: .\cookbook\models.py:912 msgid "Unit Alias" -msgstr "" +msgstr "Pseudonim unități" #: .\cookbook\models.py:912 msgid "Keyword Alias" -msgstr "" +msgstr "Pseudonim cuvânt cheie" #: .\cookbook\serializer.py:157 msgid "File uploads are not enabled for this Space." -msgstr "" +msgstr "Încărcările de fișiere nu sunt permise pentru acest spațiu." #: .\cookbook\serializer.py:168 msgid "You have reached your file upload limit." -msgstr "" +msgstr "Ați atins limita de încărcare a fișierelor." #: .\cookbook\tables.py:35 .\cookbook\templates\generic\edit_template.html:6 #: .\cookbook\templates\generic\edit_template.html:14 @@ -555,7 +612,7 @@ msgstr "" #: .\cookbook\templates\shopping_list.html:40 #: .\cookbook\templates\space.html:90 msgid "Edit" -msgstr "" +msgstr "Editare" #: .\cookbook\tables.py:115 .\cookbook\tables.py:138 #: .\cookbook\templates\generic\delete_template.html:7 @@ -563,28 +620,28 @@ msgstr "" #: .\cookbook\templates\generic\edit_template.html:28 #: .\cookbook\templates\recipes_table.html:90 msgid "Delete" -msgstr "" +msgstr "Ștergere" #: .\cookbook\templates\404.html:5 msgid "404 Error" -msgstr "" +msgstr "Eroare 404" #: .\cookbook\templates\404.html:18 msgid "The page you are looking for could not be found." -msgstr "" +msgstr "Pagina pe care o căutați nu a putut fi găsită." #: .\cookbook\templates\404.html:33 msgid "Take me Home" -msgstr "" +msgstr "Du-ma acasă" #: .\cookbook\templates\404.html:35 msgid "Report a Bug" -msgstr "" +msgstr "Raportați o eroare" #: .\cookbook\templates\account\email.html:6 #: .\cookbook\templates\account\email.html:17 msgid "E-mail Addresses" -msgstr "" +msgstr "Adrese e-mail" #: .\cookbook\templates\account\email.html:12 #: .\cookbook\templates\account\password_change.html:11 @@ -593,68 +650,71 @@ msgstr "" #: .\cookbook\templates\settings.html:17 #: .\cookbook\templates\socialaccount\connections.html:10 msgid "Settings" -msgstr "" +msgstr "Setări" #: .\cookbook\templates\account\email.html:13 msgid "Email" -msgstr "" +msgstr "E-mail" #: .\cookbook\templates\account\email.html:19 msgid "The following e-mail addresses are associated with your account:" -msgstr "" +msgstr "Următoarele adrese de e-mail sunt asociate contului dvs.:" #: .\cookbook\templates\account\email.html:36 msgid "Verified" -msgstr "" +msgstr "Verificat" #: .\cookbook\templates\account\email.html:38 msgid "Unverified" -msgstr "" +msgstr "Neverificat" #: .\cookbook\templates\account\email.html:40 msgid "Primary" -msgstr "" +msgstr "Principal" #: .\cookbook\templates\account\email.html:47 msgid "Make Primary" -msgstr "" +msgstr "Setează ca principal" #: .\cookbook\templates\account\email.html:49 msgid "Re-send Verification" -msgstr "" +msgstr "Retrimite verificare" #: .\cookbook\templates\account\email.html:50 #: .\cookbook\templates\generic\delete_template.html:56 #: .\cookbook\templates\socialaccount\connections.html:44 msgid "Remove" -msgstr "" +msgstr "Elimină" #: .\cookbook\templates\account\email.html:58 msgid "Warning:" -msgstr "" +msgstr "Atenție:" #: .\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 "" +"În prezent, nu aveți nicio adresă de e-mail configurată. Ar trebui să " +"adăugați într-adevăr o adresă de e-mail, astfel încât să puteți primi " +"notificări, resetați parola etc." #: .\cookbook\templates\account\email.html:64 msgid "Add E-mail Address" -msgstr "" +msgstr "Adăugă adresa de E-mail" #: .\cookbook\templates\account\email.html:69 msgid "Add E-mail" -msgstr "" +msgstr "Adaugă E-mail" #: .\cookbook\templates\account\email.html:79 msgid "Do you really want to remove the selected e-mail address?" -msgstr "" +msgstr "Chiar doriți să eliminați adresa de e-mail selectată?" #: .\cookbook\templates\account\email_confirm.html:6 #: .\cookbook\templates\account\email_confirm.html:10 msgid "Confirm E-mail Address" -msgstr "" +msgstr "Confirmarea adresei E-mail" #: .\cookbook\templates\account\email_confirm.html:16 #, python-format @@ -664,11 +724,15 @@ msgid "" "for user %(user_display)s\n" " ." msgstr "" +"Vă rugăm să confirmați că:\n" +" %(email)s este adresa de e-mail " +"a utilizatorului %(user_display)s\n" +" ." #: .\cookbook\templates\account\email_confirm.html:22 #: .\cookbook\templates\generic\delete_template.html:71 msgid "Confirm" -msgstr "" +msgstr "Confirmă" #: .\cookbook\templates\account\email_confirm.html:29 #, python-format @@ -677,51 +741,54 @@ msgid "" " issue a new e-mail confirmation " "request." msgstr "" +"Acest link de confirmare prin e-mail a expirat sau nu este valid. Vă rugăm\n" +" să emiteți o nouă solicitare de " +"confirmare prin e-mail." #: .\cookbook\templates\account\login.html:8 .\cookbook\templates\base.html:289 msgid "Login" -msgstr "" +msgstr "Conectare" #: .\cookbook\templates\account\login.html:15 #: .\cookbook\templates\account\login.html:31 #: .\cookbook\templates\account\signup.html:69 #: .\cookbook\templates\account\signup_closed.html:15 msgid "Sign In" -msgstr "" +msgstr "Autentificare" #: .\cookbook\templates\account\login.html:32 #: .\cookbook\templates\socialaccount\signup.html:8 #: .\cookbook\templates\socialaccount\signup.html:57 msgid "Sign Up" -msgstr "" +msgstr "Înregistrare" #: .\cookbook\templates\account\login.html:36 #: .\cookbook\templates\account\login.html:37 #: .\cookbook\templates\account\password_reset.html:29 msgid "Reset My Password" -msgstr "" +msgstr "Resetarea parolei mele" #: .\cookbook\templates\account\login.html:37 msgid "Lost your password?" -msgstr "" +msgstr "V-ați pierdut parola?" #: .\cookbook\templates\account\login.html:48 msgid "Social Login" -msgstr "" +msgstr "Autentificare utilizând rețeaua socială" #: .\cookbook\templates\account\login.html:49 msgid "You can use any of the following providers to sign in." -msgstr "" +msgstr "Puteți utiliza oricare dintre următorii furnizori pentru a vă conecta." #: .\cookbook\templates\account\logout.html:5 #: .\cookbook\templates\account\logout.html:9 #: .\cookbook\templates\account\logout.html:18 msgid "Sign Out" -msgstr "" +msgstr "Deconectare" #: .\cookbook\templates\account\logout.html:11 msgid "Are you sure you want to sign out?" -msgstr "" +msgstr "Sunteți sigur că doriți să vă deconectați?" #: .\cookbook\templates\account\password_change.html:6 #: .\cookbook\templates\account\password_change.html:16 @@ -731,44 +798,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 "Schimbare parolă" #: .\cookbook\templates\account\password_change.html:12 #: .\cookbook\templates\account\password_set.html:12 #: .\cookbook\templates\settings.html:69 msgid "Password" -msgstr "" +msgstr "Parola" #: .\cookbook\templates\account\password_change.html:22 msgid "Forgot Password?" -msgstr "" +msgstr "Ai uitat parola?" #: .\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:10 msgid "Password Reset" -msgstr "" +msgstr "Resetare parolă" #: .\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 "" +"V-ați uitat parola? Introduceți adresa de e-mail de mai jos și vă vom " +"trimite un e-mail care vă permite să o resetați." #: .\cookbook\templates\account\password_reset.html:32 msgid "Password reset is disabled on this instance." -msgstr "" +msgstr "Resetarea parolei este dezactivată în această instanță." #: .\cookbook\templates\account\password_reset_done.html:16 msgid "" "We have sent you an e-mail. Please contact us if you do not receive it " "within a few minutes." msgstr "" +"V-am trimis un e-mail. Vă rugăm să ne contactați dacă nu îl primiți în " +"câteva minute." #: .\cookbook\templates\account\password_reset_from_key.html:13 msgid "Bad Token" -msgstr "" +msgstr "Token invalid" #: .\cookbook\templates\account\password_reset_from_key.html:25 #, python-format @@ -778,199 +849,206 @@ msgid "" " Please request a new " "password reset." msgstr "" +"Linkul de resetare a parolei nu a fost valid, posibil pentru că a fost deja " +"utilizat.\n" +" Vă rugam să cereți o " +"nouă resetare a parolei." #: .\cookbook\templates\account\password_reset_from_key.html:33 msgid "change password" -msgstr "" +msgstr "schimbare parolă" #: .\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 "Parola este acum schimbată." #: .\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 "Setare parolă" #: .\cookbook\templates\account\signup.html:6 msgid "Register" -msgstr "" +msgstr "Înregistrare" #: .\cookbook\templates\account\signup.html:12 msgid "Create an Account" -msgstr "" +msgstr "Create cont" #: .\cookbook\templates\account\signup.html:42 #: .\cookbook\templates\socialaccount\signup.html:33 msgid "I accept the follwoing" -msgstr "" +msgstr "Accept următoarele" #: .\cookbook\templates\account\signup.html:45 #: .\cookbook\templates\socialaccount\signup.html:36 msgid "Terms and Conditions" -msgstr "" +msgstr "Termeni și condiții" #: .\cookbook\templates\account\signup.html:48 #: .\cookbook\templates\socialaccount\signup.html:39 msgid "and" -msgstr "" +msgstr "și" #: .\cookbook\templates\account\signup.html:52 #: .\cookbook\templates\socialaccount\signup.html:43 msgid "Privacy Policy" -msgstr "" +msgstr "Politica de confidențialitate" #: .\cookbook\templates\account\signup.html:65 msgid "Create User" -msgstr "" +msgstr "Creare utilizator" #: .\cookbook\templates\account\signup.html:69 msgid "Already have an account?" -msgstr "" +msgstr "Aveți deja un cont?" #: .\cookbook\templates\account\signup_closed.html:5 #: .\cookbook\templates\account\signup_closed.html:11 msgid "Sign Up Closed" -msgstr "" +msgstr "Înscrierea închisă" #: .\cookbook\templates\account\signup_closed.html:13 msgid "We are sorry, but the sign up is currently closed." -msgstr "" +msgstr "Ne pare rău, dar înscrierea este în prezent închisă." #: .\cookbook\templates\api_info.html:5 .\cookbook\templates\base.html:279 #: .\cookbook\templates\rest_framework\api.html:11 msgid "API Documentation" -msgstr "" +msgstr "Documentare API" #: .\cookbook\templates\base.html:86 msgid "Shopping" -msgstr "" +msgstr "Cumpărături" #: .\cookbook\templates\base.html:113 msgid "Keyword" -msgstr "" +msgstr "Cuvânt cheie" #: .\cookbook\templates\base.html:137 #: .\cookbook\templates\forms\ingredients.html:24 #: .\cookbook\templates\space.html:41 .\cookbook\templates\stats.html:26 #: .\cookbook\views\lists.py:146 msgid "Units" -msgstr "" +msgstr "Unități" #: .\cookbook\templates\base.html:151 #: .\cookbook\templates\shopping_list.html:237 #: .\cookbook\templates\supermarket.html:7 msgid "Supermarket" -msgstr "" +msgstr "Supermarket" #: .\cookbook\templates\base.html:163 msgid "Supermarket Category" -msgstr "" +msgstr "Categorie supermarket" #: .\cookbook\templates\base.html:175 .\cookbook\views\lists.py:195 msgid "Automations" -msgstr "" +msgstr "Automatizări" #: .\cookbook\templates\base.html:189 .\cookbook\views\lists.py:215 msgid "Files" -msgstr "" +msgstr "Fișiere" #: .\cookbook\templates\base.html:201 msgid "Batch Edit" -msgstr "" +msgstr "Editare în mod batch" #: .\cookbook\templates\base.html:213 .\cookbook\templates\history.html:6 #: .\cookbook\templates\history.html:14 msgid "History" -msgstr "" +msgstr "Istoric" #: .\cookbook\templates\base.html:228 .\cookbook\templates\export.html:14 #: .\cookbook\templates\export.html:20 #: .\cookbook\templates\shopping_list.html:358 #: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20 msgid "Export" -msgstr "" +msgstr "Exportă" #: .\cookbook\templates\base.html:244 .\cookbook\templates\index.html:47 msgid "Import Recipe" -msgstr "" +msgstr "Importă rețeta" #: .\cookbook\templates\base.html:246 #: .\cookbook\templates\shopping_list.html:195 #: .\cookbook\templates\shopping_list.html:217 msgid "Create" -msgstr "" +msgstr "Creează" #: .\cookbook\templates\base.html:259 #: .\cookbook\templates\generic\list_template.html:14 #: .\cookbook\templates\space.html:58 .\cookbook\templates\stats.html:43 msgid "External Recipes" -msgstr "" +msgstr "Rețete externe" #: .\cookbook\templates\base.html:262 .\cookbook\templates\space.html:7 #: .\cookbook\templates\space.html:19 msgid "Space Settings" -msgstr "" +msgstr "Setări spațiu" #: .\cookbook\templates\base.html:267 .\cookbook\templates\system.html:13 msgid "System" -msgstr "" +msgstr "Sistem" #: .\cookbook\templates\base.html:269 msgid "Admin" -msgstr "" +msgstr "Admin" #: .\cookbook\templates\base.html:273 msgid "Markdown Guide" -msgstr "" +msgstr "Ghid Markdown" #: .\cookbook\templates\base.html:275 msgid "GitHub" -msgstr "" +msgstr "GitHub" #: .\cookbook\templates\base.html:277 msgid "Translate Tandoor" -msgstr "" +msgstr "Traducere Tandoor" #: .\cookbook\templates\base.html:281 msgid "API Browser" -msgstr "" +msgstr "Browser API" #: .\cookbook\templates\base.html:284 msgid "Log out" -msgstr "" +msgstr "Deconectare" #: .\cookbook\templates\batch\edit.html:6 msgid "Batch edit Category" -msgstr "" +msgstr "Editează categoria in mod batch" #: .\cookbook\templates\batch\edit.html:15 msgid "Batch edit Recipes" -msgstr "" +msgstr "Editează rețetele in mod batch" #: .\cookbook\templates\batch\edit.html:20 msgid "Add the specified keywords to all recipes containing a word" msgstr "" +"Adăugați cuvintele cheie specificate la toate rețetele care conțin un cuvânt" #: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:82 msgid "Sync" -msgstr "" +msgstr "Sincronizare" #: .\cookbook\templates\batch\monitor.html:10 msgid "Manage watched Folders" -msgstr "" +msgstr "Gestionarea folderelor urmărite" #: .\cookbook\templates\batch\monitor.html:14 msgid "" "On this Page you can manage all storage folder locations that should be " "monitored and synced." msgstr "" +"Pe această Pagină puteți gestiona toate locațiile folderului de stocare care " +"ar trebui monitorizate și sincronizate." #: .\cookbook\templates\batch\monitor.html:16 msgid "The path must be in the following format" -msgstr "" +msgstr "Calea trebuie să fie în următorul format" #: .\cookbook\templates\batch\monitor.html:20 #: .\cookbook\templates\forms\edit_import_recipe.html:14 @@ -982,55 +1060,57 @@ msgstr "" #: .\cookbook\templates\settings.html:195 #: .\cookbook\templates\shopping_list.html:360 msgid "Save" -msgstr "" +msgstr "Salvare" #: .\cookbook\templates\batch\monitor.html:21 msgid "Manage External Storage" -msgstr "" +msgstr "Gestionarea spațiului de stocare extern" #: .\cookbook\templates\batch\monitor.html:28 msgid "Sync Now!" -msgstr "" +msgstr "Sincronizează acum!" #: .\cookbook\templates\batch\monitor.html:29 msgid "Show Recipes" -msgstr "" +msgstr "Afișare rețete" #: .\cookbook\templates\batch\monitor.html:30 msgid "Show Log" -msgstr "" +msgstr "Afișare jurnal" #: .\cookbook\templates\batch\waiting.html:4 #: .\cookbook\templates\batch\waiting.html:10 msgid "Importing Recipes" -msgstr "" +msgstr "Importă rețete" #: .\cookbook\templates\batch\waiting.html:28 msgid "" "This can take a few minutes, depending on the number of recipes in sync, " "please wait." msgstr "" +"Acest lucru poate dura câteva minute, în funcție de numărul de rețete " +"sincronizate, vă rugăm să așteptați." #: .\cookbook\templates\books.html:7 msgid "Recipe Books" -msgstr "" +msgstr "Cărți de rețete" #: .\cookbook\templates\export.html:6 .\cookbook\templates\test2.html:6 msgid "Export Recipes" -msgstr "" +msgstr "Exportă rețete" #: .\cookbook\templates\forms\edit_import_recipe.html:5 #: .\cookbook\templates\forms\edit_import_recipe.html:9 msgid "Import new Recipe" -msgstr "" +msgstr "Importă rețetă nouă" #: .\cookbook\templates\forms\edit_internal_recipe.html:7 msgid "Edit Recipe" -msgstr "" +msgstr "Editează rețetă" #: .\cookbook\templates\forms\ingredients.html:15 msgid "Edit Ingredients" -msgstr "" +msgstr "Editează ingrediente" #: .\cookbook\templates\forms\ingredients.html:16 msgid "" @@ -1042,104 +1122,112 @@ msgid "" "them.\n" " " msgstr "" +"\n" +" Următorul formular poate fi utilizat dacă, accidental, două (sau mai " +"multe) unități sau ingrediente în cazul în care au fost create care ar " +"trebui să fie\n" +" la fel.\n" +" Îmbină două unități sau ingrediente și actualizează toate rețetele " +"folosindu-le.\n" +" " #: .\cookbook\templates\forms\ingredients.html:26 msgid "Are you sure that you want to merge these two units?" -msgstr "" +msgstr "Sunteți sigur că doriți să uniți aceste două unități?" #: .\cookbook\templates\forms\ingredients.html:31 #: .\cookbook\templates\forms\ingredients.html:40 msgid "Merge" -msgstr "" +msgstr "Unire" #: .\cookbook\templates\forms\ingredients.html:36 msgid "Are you sure that you want to merge these two ingredients?" -msgstr "" +msgstr "Sunteți sigur că doriți să uniți aceste două ingrediente?" #: .\cookbook\templates\generic\delete_template.html:21 #, python-format msgid "Are you sure you want to delete the %(title)s: %(object)s " -msgstr "" +msgstr "Sunteți sigur că doriți să ștergeți %(title)s: %(object)s " #: .\cookbook\templates\generic\delete_template.html:26 msgid "Protected" -msgstr "" +msgstr "Protejat" #: .\cookbook\templates\generic\delete_template.html:41 msgid "Cascade" -msgstr "" +msgstr "Cascadă" #: .\cookbook\templates\generic\delete_template.html:72 msgid "Cancel" -msgstr "" +msgstr "Anulare" #: .\cookbook\templates\generic\edit_template.html:32 msgid "View" -msgstr "" +msgstr "Vizualizare" #: .\cookbook\templates\generic\edit_template.html:36 msgid "Delete original file" -msgstr "" +msgstr "Ștergerea fișierului original" #: .\cookbook\templates\generic\list_template.html:6 #: .\cookbook\templates\generic\list_template.html:21 msgid "List" -msgstr "" +msgstr "Listă" #: .\cookbook\templates\generic\list_template.html:34 msgid "Filter" -msgstr "" +msgstr "Filtru" #: .\cookbook\templates\generic\list_template.html:39 msgid "Import all" -msgstr "" +msgstr "Importare toate" #: .\cookbook\templates\generic\table_template.html:76 #: .\cookbook\templates\recipes_table.html:121 msgid "previous" -msgstr "" +msgstr "precedent" #: .\cookbook\templates\generic\table_template.html:98 #: .\cookbook\templates\recipes_table.html:143 msgid "next" -msgstr "" +msgstr "următor" #: .\cookbook\templates\history.html:20 msgid "View Log" -msgstr "" +msgstr "Vizionare jurnal" #: .\cookbook\templates\history.html:24 msgid "Cook Log" -msgstr "" +msgstr "Jurnal de pregătire" #: .\cookbook\templates\import.html:6 .\cookbook\templates\test.html:6 msgid "Import Recipes" -msgstr "" +msgstr "Importă rețete" #: .\cookbook\templates\include\log_cooking.html:7 msgid "Log Recipe Cooking" -msgstr "" +msgstr "Jurnal rețetă de gătire" #: .\cookbook\templates\include\log_cooking.html:13 msgid "All fields are optional and can be left empty." -msgstr "" +msgstr "Toate câmpurile sunt opționale și pot fi lăsate goale." #: .\cookbook\templates\include\log_cooking.html:19 msgid "Rating" -msgstr "" +msgstr "Evaluare" #: .\cookbook\templates\include\log_cooking.html:27 #: .\cookbook\templates\include\recipe_open_modal.html:18 msgid "Close" -msgstr "" +msgstr "Închide" #: .\cookbook\templates\include\recipe_open_modal.html:32 msgid "Open Recipe" -msgstr "" +msgstr "Deschide rețetă" #: .\cookbook\templates\include\storage_backend_warning.html:4 msgid "Security Warning" -msgstr "" +msgstr "Avertizare de securitate" #: .\cookbook\templates\include\storage_backend_warning.html:5 msgid "" @@ -1153,40 +1241,49 @@ msgid "" "can be used.\n" " " msgstr "" +"\n" +" Câmpurile de Parolă și Token sunt stocate ca text simplu în interiorul bazei de date.\n" +" Acest lucru este necesar deoarece acestea sunt necesare pentru a " +"face cereri API, dar, de asemenea, crește riscul de\n" +" furt.
    \n" +" Limitarea posibilelor deteriorări ale token-urilor sau conturilor cu " +"acces limitat ce pot fi utilizate.\n" +" " #: .\cookbook\templates\index.html:29 msgid "Search recipe ..." -msgstr "" +msgstr "Căutare rețetă ..." #: .\cookbook\templates\index.html:44 msgid "New Recipe" -msgstr "" +msgstr "Rețetă nouă" #: .\cookbook\templates\index.html:53 msgid "Advanced Search" -msgstr "" +msgstr "Căutare avansată" #: .\cookbook\templates\index.html:57 msgid "Reset Search" -msgstr "" +msgstr "Resetarea căutării" #: .\cookbook\templates\index.html:85 msgid "Last viewed" -msgstr "" +msgstr "Ultima vizualizare" #: .\cookbook\templates\index.html:87 .\cookbook\templates\space.html:35 #: .\cookbook\templates\stats.html:22 msgid "Recipes" -msgstr "" +msgstr "Rețete" #: .\cookbook\templates\index.html:94 msgid "Log in to view recipes" -msgstr "" +msgstr "Conectați-vă pentru a vizualiza rețetele" #: .\cookbook\templates\markdown_info.html:5 #: .\cookbook\templates\markdown_info.html:13 msgid "Markdown Info" -msgstr "" +msgstr "Informații Markdown" #: .\cookbook\templates\markdown_info.html:14 msgid "" @@ -1203,54 +1300,70 @@ msgid "" "below.\n" " " msgstr "" +"\n" +" Markdown este un limbaj de marcare ușor, care poate fi folosit " +"pentru formatarea textul simplu cu ușurință.\n" +" Acest site utilizează biblioteca Python Markdown pentru a\n" +" converti textului într-un document HTML frumos stilizat. " +"Documentația completă Markdown poate fi găsită\n" +" aici.\n" +" O documentație incompletă, dar cel mai probabil suficientă poate fi " +"găsită mai jos.\n" +" " #: .\cookbook\templates\markdown_info.html:25 msgid "Headers" -msgstr "" +msgstr "Anteturi" #: .\cookbook\templates\markdown_info.html:54 msgid "Formatting" -msgstr "" +msgstr "Formatare" #: .\cookbook\templates\markdown_info.html:56 #: .\cookbook\templates\markdown_info.html:72 msgid "Line breaks are inserted by adding two spaces after the end of a line" msgstr "" +"Sfârșiturile de linie sunt inserate prin adăugarea a două spații după " +"sfârșitul unei linii" #: .\cookbook\templates\markdown_info.html:57 #: .\cookbook\templates\markdown_info.html:73 msgid "or by leaving a blank line inbetween." -msgstr "" +msgstr "sau lăsând o linie goală între ele." #: .\cookbook\templates\markdown_info.html:59 #: .\cookbook\templates\markdown_info.html:74 msgid "This text is bold" -msgstr "" +msgstr "Acest text este îngroșat" #: .\cookbook\templates\markdown_info.html:60 #: .\cookbook\templates\markdown_info.html:75 msgid "This text is italic" -msgstr "" +msgstr "Acest text este cursiv" #: .\cookbook\templates\markdown_info.html:61 #: .\cookbook\templates\markdown_info.html:77 msgid "Blockquotes are also possible" -msgstr "" +msgstr "Ghilimelele bloc sunt, de asemenea, posibile" #: .\cookbook\templates\markdown_info.html:84 msgid "Lists" -msgstr "" +msgstr "Liste" #: .\cookbook\templates\markdown_info.html:85 msgid "" "Lists can ordered or unorderd. It is important to leave a blank line " "before the list!" msgstr "" +"Listele pot fi ordonate sau neordonate. Este important să lăsați o linie " +"goală înainte de listă!" #: .\cookbook\templates\markdown_info.html:87 #: .\cookbook\templates\markdown_info.html:108 msgid "Ordered List" -msgstr "" +msgstr "Listă ordonată" #: .\cookbook\templates\markdown_info.html:89 #: .\cookbook\templates\markdown_info.html:90 @@ -1259,12 +1372,12 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:111 #: .\cookbook\templates\markdown_info.html:112 msgid "unordered list item" -msgstr "" +msgstr "element a listei neordonate" #: .\cookbook\templates\markdown_info.html:93 #: .\cookbook\templates\markdown_info.html:114 msgid "Unordered List" -msgstr "" +msgstr "Listă neordonată" #: .\cookbook\templates\markdown_info.html:95 #: .\cookbook\templates\markdown_info.html:96 @@ -1273,26 +1386,29 @@ msgstr "" #: .\cookbook\templates\markdown_info.html:117 #: .\cookbook\templates\markdown_info.html:118 msgid "ordered list item" -msgstr "" +msgstr "element a listei ordonate" #: .\cookbook\templates\markdown_info.html:125 msgid "Images & Links" -msgstr "" +msgstr "Imagini și link-uri" #: .\cookbook\templates\markdown_info.html:126 msgid "" "Links can be formatted with Markdown. This application also allows to paste " "links directly into markdown fields without any formatting." msgstr "" +"Link-urile pot fi formatate cu Markdown. Această aplicație permite, de " +"asemenea, să lipiți linkuri direct în câmpurile de marcare fără nicio " +"formatare." #: .\cookbook\templates\markdown_info.html:132 #: .\cookbook\templates\markdown_info.html:145 msgid "This will become an image" -msgstr "" +msgstr "Aceasta va deveni o imagine" #: .\cookbook\templates\markdown_info.html:152 msgid "Tables" -msgstr "" +msgstr "Tabele" #: .\cookbook\templates\markdown_info.html:153 msgid "" @@ -1300,175 +1416,190 @@ msgid "" "editor like this one." msgstr "" +"Tabelele Markdown sunt cu greu create de mână. Este recomandat folosirea " +"unor editoare de tabele precum acesta." #: .\cookbook\templates\markdown_info.html:155 #: .\cookbook\templates\markdown_info.html:157 #: .\cookbook\templates\markdown_info.html:171 #: .\cookbook\templates\markdown_info.html:177 msgid "Table" -msgstr "" +msgstr "Tabel" #: .\cookbook\templates\markdown_info.html:155 #: .\cookbook\templates\markdown_info.html:172 msgid "Header" -msgstr "" +msgstr "Antet" #: .\cookbook\templates\markdown_info.html:157 #: .\cookbook\templates\markdown_info.html:178 msgid "Cell" -msgstr "" +msgstr "Celulă" #: .\cookbook\templates\meal_plan_entry.html:6 msgid "Meal Plan View" -msgstr "" +msgstr "Vizualizarea planului de alimentare" #: .\cookbook\templates\meal_plan_entry.html:18 msgid "Created by" -msgstr "" +msgstr "Creat de" #: .\cookbook\templates\meal_plan_entry.html:20 #: .\cookbook\templates\shopping_list.html:261 msgid "Shared with" -msgstr "" +msgstr "Partajat cu" #: .\cookbook\templates\meal_plan_entry.html:48 #: .\cookbook\templates\recipes_table.html:64 msgid "Last cooked" -msgstr "" +msgstr "Ultima gătită" #: .\cookbook\templates\meal_plan_entry.html:50 msgid "Never cooked before." -msgstr "" +msgstr "Niciodată gătită înainte." #: .\cookbook\templates\meal_plan_entry.html:76 msgid "Other meals on this day" -msgstr "" +msgstr "Alte mese în această zi" #: .\cookbook\templates\no_groups_info.html:5 #: .\cookbook\templates\no_groups_info.html:12 msgid "No Permissions" -msgstr "" +msgstr "Fără permisiuni" #: .\cookbook\templates\no_groups_info.html:17 msgid "You do not have any groups and therefor cannot use this application." msgstr "" +"Nu aveți nici un grup și de aceea nu se poate utiliza această aplicație." #: .\cookbook\templates\no_groups_info.html:18 #: .\cookbook\templates\no_perm_info.html:15 msgid "Please contact your administrator." -msgstr "" +msgstr "Vă rugăm să contactați administratorul." #: .\cookbook\templates\no_perm_info.html:5 #: .\cookbook\templates\no_perm_info.html:12 msgid "No Permission" -msgstr "" +msgstr "Fără permisiune" #: .\cookbook\templates\no_perm_info.html:15 msgid "" "You do not have the required permissions to view this page or perform this " "action." msgstr "" +"Nu aveți permisiunile necesare pentru a vizualiza această pagină sau pentru " +"a efectua această acțiune." #: .\cookbook\templates\no_space_info.html:6 #: .\cookbook\templates\no_space_info.html:13 msgid "No Space" -msgstr "" +msgstr "Fără spațiu" #: .\cookbook\templates\no_space_info.html:17 msgid "" "Recipes, foods, shopping lists and more are organized in spaces of one or " "more people." msgstr "" +"Rețetele, alimentele, listele de cumpărături și multe altele sunt organizate " +"în spațiile uneia sau mai multor persoane." #: .\cookbook\templates\no_space_info.html:18 msgid "" "You can either be invited into an existing space or create your own one." msgstr "" +"Puteți fi invitat într-un spațiu existent sau puteți crea propriul spațiu." #: .\cookbook\templates\no_space_info.html:31 #: .\cookbook\templates\no_space_info.html:40 msgid "Join Space" -msgstr "" +msgstr "Alăturați-vă spațiului" #: .\cookbook\templates\no_space_info.html:34 msgid "Join an existing space." -msgstr "" +msgstr "Alăturați-vă unui spațiu existent." #: .\cookbook\templates\no_space_info.html:35 msgid "" "To join an existing space either enter your invite token or click on the " "invite link the space owner send you." msgstr "" +"Pentru a vă alătura unui spațiu existent, introduceți simbolul de invitație " +"sau faceți clic pe linkul de invitație pe care vi-l trimite proprietarul " +"spațiului." #: .\cookbook\templates\no_space_info.html:48 #: .\cookbook\templates\no_space_info.html:56 msgid "Create Space" -msgstr "" +msgstr "Creare spațiu" #: .\cookbook\templates\no_space_info.html:51 msgid "Create your own recipe space." -msgstr "" +msgstr "Creați-vă propriul spațiu de rețete." #: .\cookbook\templates\no_space_info.html:52 msgid "Start your own recipe space and invite other users to it." msgstr "" +"Începeți propriul spațiu de rețete și invitați alți utilizatori la acesta." #: .\cookbook\templates\offline.html:6 msgid "Offline" -msgstr "" +msgstr "Offline" #: .\cookbook\templates\offline.html:19 msgid "You are currently offline!" -msgstr "" +msgstr "Sunteți în prezent offline!" #: .\cookbook\templates\offline.html:20 msgid "" "The recipes listed below are available for offline viewing because you have " "recently viewed them. Keep in mind that data might be outdated." msgstr "" +"Rețetele enumerate mai jos sunt disponibile pentru vizualizare offline, " +"deoarece le-ați vizualizat recent. Rețineți că datele pot fi învechite." #: .\cookbook\templates\recipe_view.html:26 msgid "by" -msgstr "" +msgstr "de" #: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:147 #: .\cookbook\views\edit.py:180 msgid "Comment" -msgstr "" +msgstr "Comentariu" #: .\cookbook\templates\recipes_table.html:19 #: .\cookbook\templates\recipes_table.html:23 #: .\cookbook\templates\url_import.html:444 msgid "Recipe Image" -msgstr "" +msgstr "Imagine rețetă" #: .\cookbook\templates\recipes_table.html:51 #: .\cookbook\templates\url_import.html:449 msgid "Preparation time ca." -msgstr "" +msgstr "Timp de pregătire cca." #: .\cookbook\templates\recipes_table.html:57 #: .\cookbook\templates\url_import.html:454 msgid "Waiting time ca." -msgstr "" +msgstr "Timp de așteptare cca." #: .\cookbook\templates\recipes_table.html:60 msgid "External" -msgstr "" +msgstr "Extern" #: .\cookbook\templates\recipes_table.html:86 msgid "Log Cooking" -msgstr "" +msgstr "Jurnal de pregătire" #: .\cookbook\templates\rest_framework\api.html:5 msgid "Recipe Home" -msgstr "" +msgstr "Rețetă acasă" #: .\cookbook\templates\search_info.html:5 #: .\cookbook\templates\search_info.html:9 #: .\cookbook\templates\settings.html:165 msgid "Search Settings" -msgstr "" +msgstr "Setări de căutare" #: .\cookbook\templates\search_info.html:10 msgid "" @@ -1481,10 +1612,18 @@ msgid "" "only available if you are using Postgres for your database.\n" " " msgstr "" +"\n" +" Crearea celei mai bune experiențe de căutare este complicată și " +"cântărește foarte mult asupra configurației personale. \n" +" Modificarea oricăreia dintre setările de căutare poate avea un " +"impact semnificativ asupra vitezei și calității rezultatelor.\n" +" Configurațiile metode de căutare, trigrame și căutare text complet " +"sunt disponibile numai dacă utilizați Postgres pentru baza de date.\n" +" " #: .\cookbook\templates\search_info.html:19 msgid "Search Methods" -msgstr "" +msgstr "Metode de căutare" #: .\cookbook\templates\search_info.html:23 msgid "" @@ -1500,6 +1639,18 @@ msgid "" "html#TEXTSEARCH-PARSING-QUERIES>Postgresql's website.\n" " " msgstr "" +" \n" +" Căutările de text complet încearcă să normalizeze cuvintele " +"furnizate pentru a se potrivi variantelor comune. De exemplu: 'bifurcat', " +"'furcă', 'furculițe' se vor normaliza la 'furculiță'.\n" +" Există mai multe metode disponibile, descrise mai jos, care vor " +"controla modul în care comportamentul de căutare ar trebui să reacționeze " +"atunci când sunt căutate mai multe cuvinte.\n" +" Detalii tehnice complete cu privire la modul în care acestea " +"funcționează pot fi vizualizate pe website-ul " +"Postgresql.\n" +" " #: .\cookbook\templates\search_info.html:29 msgid "" @@ -1511,6 +1662,14 @@ msgid "" "selected for a full text search.\n" " " msgstr "" +" \n" +" Căutările simple ignoră semnele de punctuație și cuvintele " +"comune, cum ar fi 'de', 'și', 'a', și va trata cuvinte separate după cum " +"este necesar.\n" +" Căutarea 'măr sau făină' va returna orice rețetă care include " +"atât 'măr', cât și 'făină' oriunde în câmpurile care au fost selectate " +"pentru o căutare completă de text.\n" +" " #: .\cookbook\templates\search_info.html:34 msgid "" @@ -1522,6 +1681,13 @@ msgid "" "been selected for a full text search.\n" " " msgstr "" +" \n" +" Căutările de expresii ignoră semnele de punctuație, dar vor " +"căuta toate cuvintele în ordinea exactă furnizată.\n" +" Căutarea 'măr sau făină' va returna doar o rețetă care include " +"expresia exactă 'măr sau făină' în oricare dintre câmpurile care au fost " +"selectate pentru o căutare completă a textului.\n" +" " #: .\cookbook\templates\search_info.html:39 msgid "" @@ -1541,6 +1707,21 @@ msgid "" "recipe that has the word 'butter' in any field included.\n" " " msgstr "" +" \n" +" Căutările web simulează funcționalitatea găsită pe multe site-" +"uri de căutare web care acceptă sintaxa specială.\n" +" Plasarea ghilimelelor în jurul mai multor cuvinte va converti " +"aceste cuvinte într-o frază.\n" +" 'sau' este recunoscut pentru căutarea pentru cuvântul (sau fraza)" +" imediat înainte de 'sau' SAU cuvântul (sau fraza) direct după.\n" +" '-' este recunoscut pentru căutarea rețetelor care nu includ " +"cuvântul (sau fraza) care vine imediat după. \n" +" De exemplu, căutarea 'plăcintei cu mere' sau a untului de cireșe " +"va returna orice rețetă care include expresia 'plăcintă cu mere' sau " +"cuvântul 'cireș' \n" +" în orice câmp inclus în căutarea completă a textului, dar " +"exclude orice rețetă care are cuvântul 'unt' în orice câmp inclus.\n" +" " #: .\cookbook\templates\search_info.html:48 msgid "" @@ -1549,6 +1730,10 @@ msgid "" "operators such as '|', '&' and '()'\n" " " msgstr "" +" \n" +" Căutarea brută este similară cu web-ul, cu excepția caracterelor " +"speciale, cum ar fi '|', '&' și '()'\n" +" " #: .\cookbook\templates\search_info.html:59 msgid "" @@ -1564,10 +1749,21 @@ msgid "" "methods.\n" " " msgstr "" +" \n" +" O altă abordare a căutării care, de asemenea, necesită " +"Postgresql este căutarea vagă, sau similitudinea trigramelor. O trigramă " +"este un grup de trei caractere consecutive.\n" +" De exemplu, căutarea 'mărului' va crea x trigrame 'măr', 'ăru', " +"'rul' și va crea un scor al cât de strâns se potrivesc cuvintele cu " +"trigramele generate.\n" +" Un beneficiu de căutare a trigramelor este că o căutare pentru " +"'plăcintă' va găsi cuvinte scrise greșit, cum ar fi \"plăcine\", care ar fi " +"ratat de alte metode.\n" +" " #: .\cookbook\templates\search_info.html:69 msgid "Search Fields" -msgstr "" +msgstr "Câmpuri de căutare" #: .\cookbook\templates\search_info.html:73 msgid "" @@ -1603,10 +1799,43 @@ msgid "" "full text results, it does match the trigram results.\n" " " msgstr "" +" \n" +" Unaccent este un caz special prin faptul că permite căutarea " +"unui câmp 'neaccentuat' pentru fiecare stil de căutare care încearcă să " +"ignore valorile accentuate. \n" +" De exemplu, atunci când activați unaccent pentru 'Nume', orice " +"căutare (începe cu, conține, trigramă) va încerca căutarea ignorând " +"caracterele accentuate.\n" +" \n" +" Pentru celelalte opțiuni, puteți activa căutarea pe oricare sau " +"pe toate câmpurile și acestea vor fi combinate împreună cu un presupus 'SAU'." +"\n" +" De exemplu, activarea 'Nume' pentru începe cu, 'Nume' și " +"'Descriere' pentru potrivire parțială și 'Ingrediente' și 'Cuvinte cheie' " +"pentru căutare completă\n" +" și căutarea de 'mărului' va genera o căutare care va returna " +"rețete care au:\n" +" - Un nume de rețetă care începe cu 'măr'\n" +" - SAU un nume de rețetă care conține 'măr''\n" +" - SAU o descriere a rețetei care conține 'măr'\n" +" - SAU o rețetă care va avea o potrivire completă de căutare text " +"('măr' sau 'mere') în ingrediente\n" +" - SAU o rețetă care va avea un text complet de căutare se " +"potrivesc în cuvinte cheie\n" +"\n" +" Combinarea prea multor câmpuri în prea multe tipuri de căutare " +"poate avea un impact negativ asupra performanței, poate crea rezultate " +"dublate sau poate returna rezultate neașteptate.\n" +" De exemplu, activarea căutării vage sau a potrivirilor parțiale " +"va interfera cu metodele de căutare pe web. \n" +" Căutarea 'plăcinte -mere' cu căutare vagă și căutare text " +"complet va returna rețeta plăcintelor cu mere. Deși nu este inclus în " +"rezultatele textului complet, se potrivește cu rezultatele trigramei.\n" +" " #: .\cookbook\templates\search_info.html:95 msgid "Search Index" -msgstr "" +msgstr "Index de căutare" #: .\cookbook\templates\search_info.html:99 msgid "" @@ -1620,89 +1849,108 @@ msgid "" "the management command 'python manage.py rebuildindex'\n" " " msgstr "" +" \n" +" Trigrama de căutare și full text search ambele se bazează pe " +"indexurile bazei de date pentru a efectua în mod eficient. \n" +" Puteți reconstrui indexurile pe toate câmpurile din pagina Admin " +"pentru rețete și selectând toate rețetele și rulând 'reconstrui index pentru " +"rețetele selectate'\n" +" De asemenea, puteți reconstrui indexurile la linia de comandă " +"executând comanda de gestionare 'python manage.py rebuildindex'\n" +" " #: .\cookbook\templates\settings.html:28 msgid "Account" -msgstr "" +msgstr "Cont" #: .\cookbook\templates\settings.html:35 msgid "Preferences" -msgstr "" +msgstr "Preferințe" #: .\cookbook\templates\settings.html:42 msgid "API-Settings" -msgstr "" +msgstr "Setări API" #: .\cookbook\templates\settings.html:49 msgid "Search-Settings" -msgstr "" +msgstr "Setări de căutare" #: .\cookbook\templates\settings.html:58 msgid "Name Settings" -msgstr "" +msgstr "Setări de nume" #: .\cookbook\templates\settings.html:66 msgid "Account Settings" -msgstr "" +msgstr "Setpri cont" #: .\cookbook\templates\settings.html:68 msgid "Emails" -msgstr "" +msgstr "E-mailuri" #: .\cookbook\templates\settings.html:71 #: .\cookbook\templates\socialaccount\connections.html:11 msgid "Social" -msgstr "" +msgstr "Social" #: .\cookbook\templates\settings.html:84 msgid "Language" -msgstr "" +msgstr "Limbă" #: .\cookbook\templates\settings.html:114 msgid "Style" -msgstr "" +msgstr "Stil" #: .\cookbook\templates\settings.html:135 msgid "API Token" -msgstr "" +msgstr "API Token" #: .\cookbook\templates\settings.html:136 msgid "" "You can use both basic authentication and token based authentication to " "access the REST API." msgstr "" +"Puteți utiliza atât autentificarea de bază, cât și autentificarea bazată pe " +"token pentru a accesa REST API." #: .\cookbook\templates\settings.html:153 msgid "" "Use the token as an Authorization header prefixed by the word token as shown " "in the following examples:" msgstr "" +"Utilizați token-ul ca antet de autorizare prefixat de token-ul cuvântului, " +"așa cum se arată în următoarele exemple:" #: .\cookbook\templates\settings.html:155 msgid "or" -msgstr "" +msgstr "sau" #: .\cookbook\templates\settings.html:166 msgid "" "There are many options to configure the search depending on your personal " "preferences." msgstr "" +"Există multe opțiuni pentru a configura căutarea în funcție de preferințele " +"personale." #: .\cookbook\templates\settings.html:167 msgid "" "Usually you do not need to configure any of them and can just stick " "with either the default or one of the following presets." msgstr "" +"De obicei, nu aveți nevoie să configurați niciuna dintre ele și poate " +"rămâne doar cu implicit sau una dintre următoarele presetări." #: .\cookbook\templates\settings.html:168 msgid "" "If you do want to configure the search you can read about the different " "options here." msgstr "" +"Dacă doriți să configurați căutarea, puteți citi despre diferitele opțiuni " +"aici." #: .\cookbook\templates\settings.html:173 msgid "Fuzzy" -msgstr "" +msgstr "Vag" #: .\cookbook\templates\settings.html:174 msgid "" @@ -1710,84 +1958,91 @@ msgid "" "return more results than needed to make sure you find what you are looking " "for." msgstr "" +"Găsiți ceea ce aveți nevoie, chiar dacă căutarea sau rețeta conține greșeli " +"de scriere. S-ar putea returna mai multe rezultate decât este necesar pentru " +"a vă asigura că găsiți ceea ce căutați." #: .\cookbook\templates\settings.html:175 msgid "This is the default behavior" -msgstr "" +msgstr "Acesta este comportamentul implicit" #: .\cookbook\templates\settings.html:176 #: .\cookbook\templates\settings.html:184 msgid "Apply" -msgstr "" +msgstr "Aplică" #: .\cookbook\templates\settings.html:181 msgid "Precise" -msgstr "" +msgstr "Precis" #: .\cookbook\templates\settings.html:182 msgid "" "Allows fine control over search results but might not return results if too " "many spelling mistakes are made." msgstr "" +"Permite un control fin asupra rezultatelor căutării, dar este posibil să nu " +"returneze rezultate dacă se fac prea multe greșeli de ortografie." #: .\cookbook\templates\settings.html:183 msgid "Perfect for large Databases" -msgstr "" +msgstr "Perfect pentru bazele de date mari" #: .\cookbook\templates\setup.html:6 .\cookbook\templates\system.html:5 msgid "Cookbook Setup" -msgstr "" +msgstr "Setarea cărții de bucate" #: .\cookbook\templates\setup.html:14 msgid "Setup" -msgstr "" +msgstr "Setare" #: .\cookbook\templates\setup.html:15 msgid "" "To start using this application you must first create a superuser account." msgstr "" +"Pentru a începe să utilizați această aplicație, trebuie mai întâi să creați " +"un cont superuser." #: .\cookbook\templates\setup.html:20 msgid "Create Superuser account" -msgstr "" +msgstr "Creează cont superuser" #: .\cookbook\templates\shopping_list.html:7 #: .\cookbook\templates\shopping_list.html:29 #: .\cookbook\templates\shopping_list.html:721 msgid "Shopping List" -msgstr "" +msgstr "Listă de cumpărături" #: .\cookbook\templates\shopping_list.html:34 msgid "Try the new shopping list" -msgstr "" +msgstr "Încercați noua listă de cumpărături" #: .\cookbook\templates\shopping_list.html:63 msgid "Search Recipe" -msgstr "" +msgstr "Căutare rețetă" #: .\cookbook\templates\shopping_list.html:86 msgid "Shopping Recipes" -msgstr "" +msgstr "Rețete de cumpărături" #: .\cookbook\templates\shopping_list.html:90 msgid "No recipes selected" -msgstr "" +msgstr "Nici o rețetă selectată" #: .\cookbook\templates\shopping_list.html:157 msgid "Entry Mode" -msgstr "" +msgstr "Mod de intrare" #: .\cookbook\templates\shopping_list.html:165 msgid "Add Entry" -msgstr "" +msgstr "Adăugare intrare" #: .\cookbook\templates\shopping_list.html:181 msgid "Amount" -msgstr "" +msgstr "Cantitate" #: .\cookbook\templates\shopping_list.html:194 msgid "Select Unit" -msgstr "" +msgstr "Selectare unitate" #: .\cookbook\templates\shopping_list.html:196 #: .\cookbook\templates\shopping_list.html:218 @@ -1796,59 +2051,62 @@ msgstr "" #: .\cookbook\templates\url_import.html:500 #: .\cookbook\templates\url_import.html:532 msgid "Select" -msgstr "" +msgstr "Selectare" #: .\cookbook\templates\shopping_list.html:216 msgid "Select Food" -msgstr "" +msgstr "Selectare mâncare" #: .\cookbook\templates\shopping_list.html:247 msgid "Select Supermarket" -msgstr "" +msgstr "Selectare supermarket" #: .\cookbook\templates\shopping_list.html:271 msgid "Select User" -msgstr "" +msgstr "Selectare utilizator" #: .\cookbook\templates\shopping_list.html:290 msgid "Finished" -msgstr "" +msgstr "Finisat" #: .\cookbook\templates\shopping_list.html:303 msgid "You are offline, shopping list might not syncronize." msgstr "" +"Sunteți offline, este posibil ca lista de cumpărături să nu se sincronizeze." #: .\cookbook\templates\shopping_list.html:368 msgid "Copy/Export" -msgstr "" +msgstr "Copiere/Exportare" #: .\cookbook\templates\shopping_list.html:372 msgid "List Prefix" -msgstr "" +msgstr "Prefix listă" #: .\cookbook\templates\socialaccount\connections.html:4 #: .\cookbook\templates\socialaccount\connections.html:15 msgid "Account Connections" -msgstr "" +msgstr "Conexiuni de cont" #: .\cookbook\templates\socialaccount\connections.html:18 msgid "" "You can sign in to your account using any of the following third party\n" " accounts:" msgstr "" +"Vă puteți conecta la cont utilizând oricare dintre următoarele terțe părți\n" +" de conturi:" #: .\cookbook\templates\socialaccount\connections.html:52 msgid "" "You currently have no social network accounts connected to this account." -msgstr "" +msgstr "În prezent, nu aveți conturi de rețea socială conectate la acest cont." #: .\cookbook\templates\socialaccount\connections.html:55 msgid "Add a 3rd Party Account" -msgstr "" +msgstr "Adăugarea unui cont a părților terțe" #: .\cookbook\templates\socialaccount\signup.html:5 msgid "Signup" -msgstr "" +msgstr "Înregistrare" #: .\cookbook\templates\socialaccount\signup.html:10 #, python-format @@ -1857,6 +2115,10 @@ msgid "" " %(provider_name)s account to login to\n" " %(site_name)s. As a final step, please complete the following form:" msgstr "" +"Sunteți pe cale de a utiliza\n" +" contul %(provider_name)s pentru a vă conecta la \n" +" %(site_name)s. Ca un pas final, vă rugăm să completați următorul " +"formular:" #: .\cookbook\templates\socialaccount\snippets\provider_list.html:23 #: .\cookbook\templates\socialaccount\snippets\provider_list.html:31 @@ -1873,100 +2135,100 @@ msgstr "" #: .\cookbook\templates\socialaccount\snippets\provider_list.html:119 #: .\cookbook\templates\socialaccount\snippets\provider_list.html:127 msgid "Sign in using" -msgstr "" +msgstr "Conectați-vă utilizând" #: .\cookbook\templates\space.html:23 msgid "Space:" -msgstr "" +msgstr "Spațiu:" #: .\cookbook\templates\space.html:24 msgid "Manage Subscription" -msgstr "" +msgstr "Gestionarea abonamentului" #: .\cookbook\templates\space.html:32 .\cookbook\templates\stats.html:19 msgid "Number of objects" -msgstr "" +msgstr "Numărul de obiecte" #: .\cookbook\templates\space.html:45 .\cookbook\templates\stats.html:30 msgid "Recipe Imports" -msgstr "" +msgstr "Importuri de rețete" #: .\cookbook\templates\space.html:53 .\cookbook\templates\stats.html:38 msgid "Objects stats" -msgstr "" +msgstr "Statistici obiecte" #: .\cookbook\templates\space.html:56 .\cookbook\templates\stats.html:41 msgid "Recipes without Keywords" -msgstr "" +msgstr "Rețete fără cuvinte cheie" #: .\cookbook\templates\space.html:60 .\cookbook\templates\stats.html:45 msgid "Internal Recipes" -msgstr "" +msgstr "Rețete interne" #: .\cookbook\templates\space.html:73 msgid "Members" -msgstr "" +msgstr "Membri" #: .\cookbook\templates\space.html:77 msgid "Invite User" -msgstr "" +msgstr "Invită utilizator" #: .\cookbook\templates\space.html:88 msgid "User" -msgstr "" +msgstr "Utilizator" #: .\cookbook\templates\space.html:89 msgid "Groups" -msgstr "" +msgstr "Grupe" #: .\cookbook\templates\space.html:105 msgid "admin" -msgstr "" +msgstr "admin" #: .\cookbook\templates\space.html:106 msgid "user" -msgstr "" +msgstr "utilizator" #: .\cookbook\templates\space.html:107 msgid "guest" -msgstr "" +msgstr "oaspete" #: .\cookbook\templates\space.html:108 msgid "remove" -msgstr "" +msgstr "eliminare" #: .\cookbook\templates\space.html:112 msgid "Update" -msgstr "" +msgstr "Actualizare" #: .\cookbook\templates\space.html:116 msgid "You cannot edit yourself." -msgstr "" +msgstr "Nu te poți edita singur pe tine." #: .\cookbook\templates\space.html:123 msgid "There are no members in your space yet!" -msgstr "" +msgstr "Încă nu există membri în spațiul dumneavoastră!" #: .\cookbook\templates\space.html:130 .\cookbook\templates\system.html:21 #: .\cookbook\views\lists.py:100 msgid "Invite Links" -msgstr "" +msgstr "Link-uri de invitație" #: .\cookbook\templates\stats.html:4 msgid "Stats" -msgstr "" +msgstr "Atribute" #: .\cookbook\templates\stats.html:10 msgid "Statistics" -msgstr "" +msgstr "Statistici" #: .\cookbook\templates\system.html:22 msgid "Show Links" -msgstr "" +msgstr "Afișează link-uri" #: .\cookbook\templates\system.html:32 msgid "System Information" -msgstr "" +msgstr "Informații despre sistem" #: .\cookbook\templates\system.html:34 msgid "" @@ -1978,20 +2240,27 @@ msgid "" "recipes/releases\">here.\n" " " msgstr "" +"\n" +" Django Recipes este o aplicație software gratuită open source. " +"Acesta poate fi găsită pe\n" +" GitHub.\n" +" Istoricul modificării poate fi găsit aici.\n" +" " #: .\cookbook\templates\system.html:48 msgid "Media Serving" -msgstr "" +msgstr "Livrare conținut media" #: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:80 msgid "Warning" -msgstr "" +msgstr "Atenționare" #: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64 #: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95 msgid "Ok" -msgstr "" +msgstr "Ok" #: .\cookbook\templates\system.html:51 msgid "" @@ -2002,15 +2271,22 @@ msgid "" " your installation.\n" " " msgstr "" +"Servirea fișierelor media direct folosind gunicorn/python nu este " +"recomandată !\n" +" Vă rugăm să urmați pașii descriși\n" +" aici pentru a actualiza\n" +" instalarea dvs..\n" +" " #: .\cookbook\templates\system.html:57 .\cookbook\templates\system.html:73 #: .\cookbook\templates\system.html:88 .\cookbook\templates\system.html:102 msgid "Everything is fine!" -msgstr "" +msgstr "Totul este bine!" #: .\cookbook\templates\system.html:62 msgid "Secret Key" -msgstr "" +msgstr "Cheie secretă" #: .\cookbook\templates\system.html:66 msgid "" @@ -2024,10 +2300,19 @@ msgid "" "file.\n" " " msgstr "" +"\n" +" Nu aveți un SECRET_KEY configurat în fișierul " +".env. Django utilizează implicit\n" +" cheia standard\n" +" prevazuta cu instalația care este cunoscuta public si nesigura! " +"Vă rugăm să setați\n" +" SECRET_KEY în fișierul de configurare ." +"env.\n" +" " #: .\cookbook\templates\system.html:78 msgid "Debug Mode" -msgstr "" +msgstr "Mod de depanare" #: .\cookbook\templates\system.html:82 msgid "" @@ -2039,14 +2324,21 @@ msgid "" "file.\n" " " msgstr "" +"\n" +" Această aplicație se execută încă în modul de depanare. Acest " +"lucru nu este cel mai probabil necesar. Rândul său, modul de depanare prin\n" +" setarea\n" +" DEBUG=0 in zona .env a fișierului de " +"configurare.\n" +" " #: .\cookbook\templates\system.html:93 msgid "Database" -msgstr "" +msgstr "Bază de date" #: .\cookbook\templates\system.html:95 msgid "Info" -msgstr "" +msgstr "Informație" #: .\cookbook\templates\system.html:97 msgid "" @@ -2056,46 +2348,52 @@ msgid "" " features only work with postgres databases.\n" " " msgstr "" +"\n" +" Această aplicație nu se execută cu un backend de bază de date " +"Postgres. Acest lucru este ok, dar nu este recomandat deoarece unele\n" +" caracteristicile funcționează numai cu baze de date Postgres.\n" +" " #: .\cookbook\templates\url_import.html:6 msgid "URL Import" -msgstr "" +msgstr "Importare URL" #: .\cookbook\templates\url_import.html:31 msgid "Drag me to your bookmarks to import recipes from anywhere" -msgstr "" +msgstr "Trageți-mă în marcajele dvs., pentru a importa rețete de oriunde" #: .\cookbook\templates\url_import.html:32 msgid "Bookmark Me!" -msgstr "" +msgstr "Marcaj-mă!" #: .\cookbook\templates\url_import.html:36 msgid "URL" -msgstr "" +msgstr "URL" #: .\cookbook\templates\url_import.html:38 msgid "App" -msgstr "" +msgstr "Aplicație" #: .\cookbook\templates\url_import.html:62 msgid "Enter website URL" -msgstr "" +msgstr "Introduceți adresa URL a site-ului web" #: .\cookbook\templates\url_import.html:101 msgid "Select recipe files to import or drop them here..." -msgstr "" +msgstr "Selectați fișierele de rețetă pentru a le importa sau a le fixa aici..." #: .\cookbook\templates\url_import.html:122 msgid "Paste json or html source here to load recipe." -msgstr "" +msgstr "Plasează sursa JSON sau HTML aici pentru a încărca rețeta." #: .\cookbook\templates\url_import.html:150 msgid "Preview Recipe Data" -msgstr "" +msgstr "Previzualizați datele rețetei" #: .\cookbook\templates\url_import.html:151 msgid "Drag recipe attributes from the right into the appropriate box below." msgstr "" +"Trageți atributele rețetei din dreapta în caseta corespunzătoare de mai jos." #: .\cookbook\templates\url_import.html:160 #: .\cookbook\templates\url_import.html:177 @@ -2108,113 +2406,116 @@ msgstr "" #: .\cookbook\templates\url_import.html:304 #: .\cookbook\templates\url_import.html:355 msgid "Clear Contents" -msgstr "" +msgstr "Curățare conținut" #: .\cookbook\templates\url_import.html:162 msgid "Text dragged here will be appended to the name." -msgstr "" +msgstr "Textul tras aici va fi adăugat la nume." #: .\cookbook\templates\url_import.html:175 msgid "Description" -msgstr "" +msgstr "Descriere" #: .\cookbook\templates\url_import.html:179 msgid "Text dragged here will be appended to the description." -msgstr "" +msgstr "Textul tras aici va fi adăugat la descriere." #: .\cookbook\templates\url_import.html:196 msgid "Keywords dragged here will be appended to current list" -msgstr "" +msgstr "Cuvintele cheie trase aici vor fi anexate la lista curentă" #: .\cookbook\templates\url_import.html:211 msgid "Image" -msgstr "" +msgstr "Imagine" #: .\cookbook\templates\url_import.html:243 msgid "Prep Time" -msgstr "" +msgstr "Timp de pregătire" #: .\cookbook\templates\url_import.html:258 msgid "Cook Time" -msgstr "" +msgstr "Timp de gătire" #: .\cookbook\templates\url_import.html:279 msgid "Ingredients dragged here will be appended to current list." -msgstr "" +msgstr "Ingredientele trase aici vor fi anexate la lista curentă." #: .\cookbook\templates\url_import.html:301 #: .\cookbook\templates\url_import.html:572 msgid "Instructions" -msgstr "" +msgstr "Instrucțiuni" #: .\cookbook\templates\url_import.html:306 msgid "" "Recipe instructions dragged here will be appended to current instructions." msgstr "" +"Instrucțiunile de rețetă trase aici vor fi anexate la instrucțiunile curente." #: .\cookbook\templates\url_import.html:329 msgid "Discovered Attributes" -msgstr "" +msgstr "Atribute descoperite" #: .\cookbook\templates\url_import.html:331 msgid "" "Drag recipe attributes from below into the appropriate box on the left. " "Click any node to display its full properties." msgstr "" +"Trageți atributele rețetei de mai jos în caseta corespunzătoare din stânga. " +"Faceți clic pe orice nod pentru a afișa proprietățile sale complete." #: .\cookbook\templates\url_import.html:348 msgid "Show Blank Field" -msgstr "" +msgstr "Afișare câmp gol" #: .\cookbook\templates\url_import.html:353 msgid "Blank Field" -msgstr "" +msgstr "Câmp gol" #: .\cookbook\templates\url_import.html:357 msgid "Items dragged to Blank Field will be appended." -msgstr "" +msgstr "Elementele trase în câmpul gol vor fi adăugate." #: .\cookbook\templates\url_import.html:404 msgid "Delete Text" -msgstr "" +msgstr "Ștergere text" #: .\cookbook\templates\url_import.html:417 msgid "Delete image" -msgstr "" +msgstr "Ștergere imagine" #: .\cookbook\templates\url_import.html:433 msgid "Recipe Name" -msgstr "" +msgstr "Nume rețetă" #: .\cookbook\templates\url_import.html:437 msgid "Recipe Description" -msgstr "" +msgstr "Descriere rețetă" #: .\cookbook\templates\url_import.html:499 #: .\cookbook\templates\url_import.html:531 #: .\cookbook\templates\url_import.html:587 msgid "Select one" -msgstr "" +msgstr "Selectați una" #: .\cookbook\templates\url_import.html:547 msgid "Note" -msgstr "" +msgstr "Notă" #: .\cookbook\templates\url_import.html:588 msgid "Add Keyword" -msgstr "" +msgstr "Adaugă cuvânt cheie" #: .\cookbook\templates\url_import.html:601 msgid "All Keywords" -msgstr "" +msgstr "Toate cuvintele cheie" #: .\cookbook\templates\url_import.html:604 msgid "Import all keywords, not only the ones already existing." -msgstr "" +msgstr "Importă toate cuvintele cheie, nu numai cele deja existente." #: .\cookbook\templates\url_import.html:631 msgid "Information" -msgstr "" +msgstr "Informație" #: .\cookbook\templates\url_import.html:633 msgid "" @@ -2226,307 +2527,332 @@ msgid "" "data feel free to post an example in the\n" " github issues." msgstr "" +" Numai site-urile web care conțin informații despre ld+json sau informații " +"microdata în prezent\n" +" pot fi importate. Majoritatea pagini " +"importante de rețete au suport pentru acest lucru. Dacă site-ul nu poate fi " +"importat, dar\n" +" crezi că\n" +" acesta are, probabil, un fel de date " +"structurate nu ezitați să posta un exemplu în\n" +" github probleme." #: .\cookbook\templates\url_import.html:641 msgid "Google ld+json Info" -msgstr "" +msgstr "Informații Google ld+json" #: .\cookbook\templates\url_import.html:644 msgid "GitHub Issues" -msgstr "" +msgstr "GitHub Probleme" #: .\cookbook\templates\url_import.html:646 msgid "Recipe Markup Specification" -msgstr "" +msgstr "Specificații a Markup-ului de rețetă" #: .\cookbook\views\api.py:83 .\cookbook\views\api.py:132 msgid "Parameter updated_at incorrectly formatted" -msgstr "" +msgstr "Parametrul updated_at formatat incorect" #: .\cookbook\views\api.py:152 #, python-brace-format msgid "No {self.basename} with id {pk} exists" -msgstr "" +msgstr "Nu există {self.basename} cu id {pk}" #: .\cookbook\views\api.py:156 msgid "Cannot merge with the same object!" -msgstr "" +msgstr "Nu se poate uni cu același obiect!" #: .\cookbook\views\api.py:163 #, python-brace-format msgid "No {self.basename} with id {target} exists" -msgstr "" +msgstr "Nu există {self.basename} cu id {target}" #: .\cookbook\views\api.py:168 msgid "Cannot merge with child object!" -msgstr "" +msgstr "Nu se poate uni cu obiect copil!" #: .\cookbook\views\api.py:201 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" -msgstr "" +msgstr "{source.name} a fost unit cu succes cu {target.name}" #: .\cookbook\views\api.py:205 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" -msgstr "" +msgstr "A apărut o eroare la încercarea de a uni {source.name} cu {target.name}" #: .\cookbook\views\api.py:249 #, python-brace-format msgid "No {self.basename} with id {child} exists" -msgstr "" +msgstr "Nu există {self.basename} cu id {child}" #: .\cookbook\views\api.py:258 #, python-brace-format msgid "{child.name} was moved successfully to the root." -msgstr "" +msgstr "{child.name} a fost mutat cu succes la rădăcină." #: .\cookbook\views\api.py:261 .\cookbook\views\api.py:279 msgid "An error occurred attempting to move " -msgstr "" +msgstr "A apărut o eroare la încercarea de a muta " #: .\cookbook\views\api.py:264 msgid "Cannot move an object to itself!" -msgstr "" +msgstr "Nu se poate muta un obiect la sine!" #: .\cookbook\views\api.py:270 #, python-brace-format msgid "No {self.basename} with id {parent} exists" -msgstr "" +msgstr "Nu există {self.basename} cu id {parent}" #: .\cookbook\views\api.py:276 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" -msgstr "" +msgstr "{child.name} a fost mutat cu succes la părintele {parent.name}" #: .\cookbook\views\api.py:723 .\cookbook\views\data.py:42 #: .\cookbook\views\edit.py:129 .\cookbook\views\new.py:95 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" +"Această funcție nu este încă disponibilă în versiunea găzduită a tandoor!" #: .\cookbook\views\api.py:745 msgid "Sync successful!" -msgstr "" +msgstr "Sincronizare de succes!" #: .\cookbook\views\api.py:750 msgid "Error synchronizing with Storage" -msgstr "" +msgstr "Eroare la sincronizarea cu stocarea" #: .\cookbook\views\api.py:828 msgid "Nothing to do." -msgstr "" +msgstr "Nimic de făcut." #: .\cookbook\views\api.py:843 msgid "The requested site provided malformed data and cannot be read." -msgstr "" +msgstr "Site-ul solicitat a furnizat date eronate și nu poate fi citit." #: .\cookbook\views\api.py:850 msgid "The requested page could not be found." -msgstr "" +msgstr "Pagina solicitată nu a putut fi găsită." #: .\cookbook\views\api.py:859 msgid "" "The requested site does not provide any recognized data format to import the " "recipe from." msgstr "" +"Site-ul solicitat nu oferă niciun format de date recunoscut din care să " +"importați rețeta." #: .\cookbook\views\api.py:873 msgid "No useable data could be found." -msgstr "" +msgstr "Nu au putut fi găsite date utilizabile." #: .\cookbook\views\api.py:889 msgid "I couldn't find anything to do." -msgstr "" +msgstr "Nu am putut găsi nimic de făcut." #: .\cookbook\views\data.py:34 .\cookbook\views\data.py:129 #: .\cookbook\views\edit.py:49 .\cookbook\views\import_export.py:73 #: .\cookbook\views\new.py:33 msgid "You have reached the maximum number of recipes for your space." -msgstr "" +msgstr "Ai ajuns la numărul maxim de rețete pentru spațiul dvs." #: .\cookbook\views\data.py:38 .\cookbook\views\data.py:133 #: .\cookbook\views\edit.py:53 .\cookbook\views\import_export.py:77 #: .\cookbook\views\new.py:37 msgid "You have more users than allowed in your space." -msgstr "" +msgstr "Aveți mai mulți utilizatori decât este permis în spațiul dvs." #: .\cookbook\views\data.py:111 #, python-format msgid "Batch edit done. %(count)d recipe was updated." msgid_plural "Batch edit done. %(count)d Recipes where updated." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Editarea de tip batch completă. %(count)d rețetă a fost actualizată." +msgstr[1] "Editarea de tip batch completă. %(count)d rețete au fost actualizate." +msgstr[2] "Editarea de tip batch completă. %(count)d rețete au fost actualizate." #: .\cookbook\views\delete.py:101 msgid "Monitor" -msgstr "" +msgstr "Monitorizare" #: .\cookbook\views\delete.py:125 .\cookbook\views\lists.py:86 #: .\cookbook\views\new.py:101 msgid "Storage Backend" -msgstr "" +msgstr "Backend de stocare" #: .\cookbook\views\delete.py:135 msgid "" "Could not delete this storage backend as it is used in at least one monitor." msgstr "" +"Nu s-a putut șterge acest backend de stocare, deoarece este utilizat în cel " +"puțin un supervizor." #: .\cookbook\views\delete.py:158 msgid "Recipe Book" -msgstr "" +msgstr "Carte de rețete" #: .\cookbook\views\delete.py:170 msgid "Bookmarks" -msgstr "" +msgstr "Marcaje" #: .\cookbook\views\delete.py:192 .\cookbook\views\new.py:235 msgid "Invite Link" -msgstr "" +msgstr "Link de invitare" #: .\cookbook\views\edit.py:125 msgid "You cannot edit this storage!" -msgstr "" +msgstr "Nu puteți edita acest spațiu de stocare!" #: .\cookbook\views\edit.py:149 msgid "Storage saved!" -msgstr "" +msgstr "Spațiu de stocare salvat!" #: .\cookbook\views\edit.py:155 msgid "There was an error updating this storage backend!" -msgstr "" +msgstr "A existat o eroare la actualizarea acestui backend de stocare!" #: .\cookbook\views\edit.py:248 msgid "Changes saved!" -msgstr "" +msgstr "Modificări salvate!" #: .\cookbook\views\edit.py:252 msgid "Error saving changes!" -msgstr "" +msgstr "Eroare la salvarea modificărilor!" #: .\cookbook\views\import_export.py:99 msgid "Importing is not implemented for this provider" -msgstr "" +msgstr "Importul nu este implementat pentru acest furnizor" #: .\cookbook\views\import_export.py:121 msgid "Exporting is not implemented for this provider" -msgstr "" +msgstr "Exportul nu este implementat pentru acest furnizor" #: .\cookbook\views\lists.py:26 msgid "Import Log" -msgstr "" +msgstr "Jurnal de import" #: .\cookbook\views\lists.py:39 msgid "Discovery" -msgstr "" +msgstr "Descoperă" #: .\cookbook\views\lists.py:69 msgid "Shopping Lists" -msgstr "" +msgstr "Liste de cumpărături" #: .\cookbook\views\lists.py:129 msgid "Foods" -msgstr "" +msgstr "Alimente" #: .\cookbook\views\lists.py:163 msgid "Supermarkets" -msgstr "" +msgstr "Supermarketuri" #: .\cookbook\views\lists.py:179 msgid "Shopping Categories" -msgstr "" +msgstr "Categorii de cumpărături" #: .\cookbook\views\lists.py:232 msgid "New Shopping List" -msgstr "" +msgstr "Listă de cumpărături nouă" #: .\cookbook\views\new.py:126 msgid "Imported new recipe!" -msgstr "" +msgstr "Rețetă nouă importată!" #: .\cookbook\views\new.py:129 msgid "There was an error importing this recipe!" -msgstr "" +msgstr "A existat o eroare la importul acestei rețete!" #: .\cookbook\views\new.py:209 msgid "Hello" -msgstr "" +msgstr "Bună" #: .\cookbook\views\new.py:209 msgid "You have been invited by " -msgstr "" +msgstr "Ați fost invitat de " #: .\cookbook\views\new.py:210 msgid " to join their Tandoor Recipes space " -msgstr "" +msgstr " pentru a vă alătura la spațiul lor de rețete Tandoor " #: .\cookbook\views\new.py:211 msgid "Click the following link to activate your account: " -msgstr "" +msgstr "Faceți clic pe următorul link pentru a vă activa contul: " #: .\cookbook\views\new.py:212 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" +"Dacă linkul nu funcționează, utilizați următorul cod pentru a vă alătura " +"manual spațiului: " #: .\cookbook\views\new.py:213 msgid "The invitation is valid until " -msgstr "" +msgstr "Invitația este valabilă până la " #: .\cookbook\views\new.py:214 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" +"Tandoor Recipes este un manager de rețete Open Source. Priviți pe GitHub " #: .\cookbook\views\new.py:217 msgid "Tandoor Recipes Invite" -msgstr "" +msgstr "Invitație Tandoor Recipes" #: .\cookbook\views\new.py:224 msgid "Invite link successfully send to user." -msgstr "" +msgstr "Link-ul invita cu succes trimite la utilizator." #: .\cookbook\views\new.py:227 msgid "" -"You have send to many emails, please share the link manually or wait a few " +"You have sent too many emails, please share the link manually or wait a few " "hours." msgstr "" +"Ați trimis prea multe e-mailuri, vă rugăm să partajați manual linkul sau să " +"așteptați câteva ore." #: .\cookbook\views\new.py:229 msgid "Email could not be sent to user. Please share the link manually." msgstr "" +"E-mailul nu a putut fi trimis utilizatorului. Vă rugăm să partajați link-ul " +"manual." #: .\cookbook\views\views.py:127 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" +"V-ați creat cu succes propriul spațiu de rețete. Începeți prin a adăuga " +"câteva rețete sau invitați alte persoane să vi se alăture." #: .\cookbook\views\views.py:175 msgid "You do not have the required permissions to perform this action!" -msgstr "" +msgstr "Nu aveți permisiunile necesare pentru a efectua această acțiune!" #: .\cookbook\views\views.py:186 msgid "Comment saved!" -msgstr "" +msgstr "Comentariu salvat!" #: .\cookbook\views\views.py:277 msgid "This feature is not available in the demo version!" -msgstr "" +msgstr "Această funcție nu este disponibilă în versiunea demo!" #: .\cookbook\views\views.py:340 msgid "You must select at least one field to search!" -msgstr "" +msgstr "Trebuie să selectați cel puțin un câmp pentru a căuta!" #: .\cookbook\views\views.py:345 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" +"Pentru a utiliza această metodă de căutare, trebuie să selectați cel puțin " +"un câmp de căutare text complet!" #: .\cookbook\views\views.py:349 msgid "Fuzzy search is not compatible with this search method!" -msgstr "" +msgstr "Căutarea vagă nu este compatibilă cu această metodă de căutare!" #: .\cookbook\views\views.py:452 msgid "" @@ -2534,39 +2860,48 @@ msgid "" "forgotten your superuser credentials please consult the django documentation " "on how to reset passwords." msgstr "" +"Pagina de inițializare poate fi utilizată numai pentru a crea primul " +"utilizator! Dacă ați uitat datele superutilizatorului, vă rugăm să " +"consultați documentația Django despre cum să resetați parolele." #: .\cookbook\views\views.py:459 msgid "Passwords dont match!" -msgstr "" +msgstr "Parolele nu se potrivesc!" #: .\cookbook\views\views.py:475 msgid "User has been created, please login!" -msgstr "" +msgstr "Utilizatorul a fost creat, vă rugăm să vă autentificați!" #: .\cookbook\views\views.py:491 msgid "Malformed Invite Link supplied!" -msgstr "" +msgstr "Link-ul de invitație este furnizat malformat!" #: .\cookbook\views\views.py:498 msgid "You are already member of a space and therefore cannot join this one." msgstr "" +"Sunteți deja membru al unui spațiu și, prin urmare, nu vă puteți alătura " +"acestuia." #: .\cookbook\views\views.py:509 msgid "Successfully joined space." -msgstr "" +msgstr "Spațiu alăturat cu succes." #: .\cookbook\views\views.py:515 msgid "Invite Link not valid or already used!" -msgstr "" +msgstr "Link-ul de invitație nu este valid sau deja utilizat!" #: .\cookbook\views\views.py:579 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" +"Raportarea linkurilor de partajare nu este activată pentru această instanță. " +"Vă rugăm să anunțați administratorul paginii pentru a raporta probleme." #: .\cookbook\views\views.py:585 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." msgstr "" +"Partajare link-urilor de rețete a fost dezactivată! Pentru informații " +"suplimentare, vă rugăm să contactați administratorul paginii." diff --git a/cookbook/locale/ru/LC_MESSAGES/django.mo b/cookbook/locale/ru/LC_MESSAGES/django.mo index b891c482d..3e7acced3 100644 Binary files a/cookbook/locale/ru/LC_MESSAGES/django.mo and b/cookbook/locale/ru/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/ru/LC_MESSAGES/django.po b/cookbook/locale/ru/LC_MESSAGES/django.po index 31c4280d7..52a4708a3 100644 --- a/cookbook/locale/ru/LC_MESSAGES/django.po +++ b/cookbook/locale/ru/LC_MESSAGES/django.po @@ -8,17 +8,17 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-09-13 22:40+0200\n" -"PO-Revision-Date: 2022-11-30 19:09+0000\n" -"Last-Translator: Alex \n" +"PO-Revision-Date: 2023-05-01 07:55+0000\n" +"Last-Translator: axeron2036 \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.14.1\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125 #: .\cookbook\templates\forms\ingredients.html:34 @@ -286,7 +286,7 @@ msgstr "" #: .\cookbook\forms.py:497 msgid "Search Method" -msgstr "" +msgstr "Способ поиска" #: .\cookbook\forms.py:498 msgid "Fuzzy Lookups" @@ -861,7 +861,7 @@ msgstr "" #: .\cookbook\templates\base.html:220 msgid "GitHub" -msgstr "" +msgstr "GitHub" #: .\cookbook\templates\base.html:224 msgid "API Browser" @@ -1937,7 +1937,7 @@ msgstr "" #: .\cookbook\templates\space.html:106 msgid "user" -msgstr "" +msgstr "пользователь" #: .\cookbook\templates\space.html:107 msgid "guest" diff --git a/cookbook/locale/sl/LC_MESSAGES/django.mo b/cookbook/locale/sl/LC_MESSAGES/django.mo index a6153c74f..fd1453e17 100644 Binary files a/cookbook/locale/sl/LC_MESSAGES/django.mo and b/cookbook/locale/sl/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/sl/LC_MESSAGES/django.po b/cookbook/locale/sl/LC_MESSAGES/django.po index cb96c830e..4aabc1020 100644 --- a/cookbook/locale/sl/LC_MESSAGES/django.po +++ b/cookbook/locale/sl/LC_MESSAGES/django.po @@ -8,17 +8,17 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-08 16:27+0100\n" -"PO-Revision-Date: 2022-02-02 15:31+0000\n" -"Last-Translator: Mario Dvorsek \n" +"PO-Revision-Date: 2023-08-13 08:19+0000\n" +"Last-Translator: Miha Perpar \n" "Language-Team: Slovenian \n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" -"%100==4 ? 2 : 3;\n" -"X-Generator: Weblate 4.10.1\n" +"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 4.15\n" #: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125 #: .\cookbook\templates\forms\ingredients.html:34 @@ -964,7 +964,7 @@ msgstr "" #: .\cookbook\templates\base.html:275 msgid "GitHub" -msgstr "" +msgstr "GitHub" #: .\cookbook\templates\base.html:277 msgid "Translate Tandoor" @@ -1961,7 +1961,7 @@ msgstr "" #: .\cookbook\templates\space.html:106 msgid "user" -msgstr "" +msgstr "uporabnik" #: .\cookbook\templates\space.html:107 msgid "guest" @@ -2107,7 +2107,7 @@ msgstr "" #: .\cookbook\templates\url_import.html:36 msgid "URL" -msgstr "" +msgstr "URL" #: .\cookbook\templates\url_import.html:38 msgid "App" diff --git a/cookbook/locale/tr/LC_MESSAGES/django.po b/cookbook/locale/tr/LC_MESSAGES/django.po index 697415333..f90083aae 100644 --- a/cookbook/locale/tr/LC_MESSAGES/django.po +++ b/cookbook/locale/tr/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" "PO-Revision-Date: 2022-11-06 22:09+0000\n" "Last-Translator: Gorkem \n" "Language-Team: Turkish /remote." "php/webdav/ is added automatically)" msgstr "" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "Aktif" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "" -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "" -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "" -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "" -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "" -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." msgstr "" -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." msgstr "" -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "" -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." msgstr "" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." msgstr "" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "" -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 #, fuzzy #| msgid "Show recently viewed recipes on search page." msgid "Show recipe counts on search filters" msgstr "Son görüntülenen tarifleri arama sayfasında göster." -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "" @@ -440,7 +440,7 @@ msgid "" msgstr "" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "" @@ -453,7 +453,7 @@ msgstr "" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "" @@ -472,11 +472,39 @@ msgstr "" msgid "You have more users than allowed in your space." msgstr "" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +msgid "reverse rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "" @@ -494,36 +522,40 @@ msgstr "" msgid "I made this" msgstr "" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "" -#: .\cookbook\integration\paprika.py:46 -msgid "Notes" +#: .\cookbook\integration\openeats.py:26 +msgid "Recipe source:" msgstr "" #: .\cookbook\integration\paprika.py:49 +msgid "Notes" +msgstr "" + +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "" @@ -590,72 +622,72 @@ msgid "" "upload." msgstr "" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr "" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "" @@ -671,64 +703,64 @@ msgstr "" msgid "Cannot modify Space owner permission." msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "" -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr "" -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "" -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "" -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "" -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "" -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "" @@ -1518,11 +1550,11 @@ msgstr "" msgid "Profile" msgstr "" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "" @@ -2000,258 +2032,258 @@ msgstr "" msgid "URL Import" msgstr "" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "" -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." msgstr "" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "" -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "" @@ -2312,7 +2344,7 @@ msgstr "" msgid "Error saving changes!" msgstr "" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2358,73 +2390,73 @@ msgstr "" msgid "There was an error importing this recipe!" msgstr "" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " "on how to reset passwords." msgstr "" -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/uk/LC_MESSAGES/django.mo b/cookbook/locale/uk/LC_MESSAGES/django.mo index 4218b426c..939847015 100644 Binary files a/cookbook/locale/uk/LC_MESSAGES/django.mo and b/cookbook/locale/uk/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/uk/LC_MESSAGES/django.po b/cookbook/locale/uk/LC_MESSAGES/django.po index 94991156d..b48ed6a51 100644 --- a/cookbook/locale/uk/LC_MESSAGES/django.po +++ b/cookbook/locale/uk/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-04-29 18:42+0200\n" -"PO-Revision-Date: 2023-02-09 13:55+0000\n" -"Last-Translator: vertilo \n" +"PO-Revision-Date: 2023-04-12 11:55+0000\n" +"Last-Translator: noxonad \n" "Language-Team: Ukrainian \n" "Language: uk\n" @@ -1091,7 +1091,7 @@ msgstr "" #: .\cookbook\templates\base.html:311 msgid "GitHub" -msgstr "" +msgstr "GitHub" #: .\cookbook\templates\base.html:313 msgid "Translate Tandoor" diff --git a/cookbook/locale/zh_CN/LC_MESSAGES/django.mo b/cookbook/locale/zh_CN/LC_MESSAGES/django.mo index fcbd4ef26..9a6271941 100644 Binary files a/cookbook/locale/zh_CN/LC_MESSAGES/django.mo and b/cookbook/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/cookbook/locale/zh_CN/LC_MESSAGES/django.po b/cookbook/locale/zh_CN/LC_MESSAGES/django.po index 19f012c03..3025787ad 100644 --- a/cookbook/locale/zh_CN/LC_MESSAGES/django.po +++ b/cookbook/locale/zh_CN/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-19 19:14+0100\n" +"POT-Creation-Date: 2023-05-18 14:28+0200\n" "PO-Revision-Date: 2023-02-26 13:15+0000\n" "Last-Translator: 吕楪 \n" "Language-Team: Chinese (Simplified) /remote." "php/webdav/ is added automatically)" @@ -193,60 +193,60 @@ msgstr "" "Dropbox 留空并输入基础 Nextcloud 网址(/remote.php/webdav/ 会自" "动添加)" -#: .\cookbook\forms.py:264 .\cookbook\views\edit.py:157 +#: .\cookbook\forms.py:282 .\cookbook\views\edit.py:157 msgid "Storage" msgstr "存储" -#: .\cookbook\forms.py:266 +#: .\cookbook\forms.py:284 msgid "Active" msgstr "活跃" -#: .\cookbook\forms.py:272 +#: .\cookbook\forms.py:290 msgid "Search String" msgstr "搜索字符串" -#: .\cookbook\forms.py:299 +#: .\cookbook\forms.py:317 msgid "File ID" msgstr "文件编号" -#: .\cookbook\forms.py:321 +#: .\cookbook\forms.py:339 msgid "You must provide at least a recipe or a title." msgstr "你必须至少提供一份菜谱或一个标题。" -#: .\cookbook\forms.py:334 +#: .\cookbook\forms.py:352 msgid "You can list default users to share recipes with in the settings." msgstr "你可以在设置中列出默认用户来分享菜谱。" -#: .\cookbook\forms.py:335 +#: .\cookbook\forms.py:353 msgid "" "You can use markdown to format this field. See the docs here" msgstr "" "可以使用 Markdown 设置此字段格式。查看文档" -#: .\cookbook\forms.py:361 +#: .\cookbook\forms.py:379 msgid "Maximum number of users for this space reached." msgstr "已达到该空间的最大用户数。" -#: .\cookbook\forms.py:367 +#: .\cookbook\forms.py:385 msgid "Email address already taken!" msgstr "电子邮件地址已被注册!" -#: .\cookbook\forms.py:375 +#: .\cookbook\forms.py:393 msgid "" "An email address is not required but if present the invite link will be sent " "to the user." msgstr "电子邮件地址不是必需的,但如果存在,邀请链接将被发送给用户。" -#: .\cookbook\forms.py:390 +#: .\cookbook\forms.py:408 msgid "Name already taken." msgstr "名字已被占用。" -#: .\cookbook\forms.py:401 +#: .\cookbook\forms.py:419 msgid "Accept Terms and Privacy" msgstr "接受条款及隐私政策" -#: .\cookbook\forms.py:433 +#: .\cookbook\forms.py:451 msgid "" "Determines how fuzzy a search is if it uses trigram similarity matching (e." "g. low values mean more typos are ignored)." @@ -254,7 +254,7 @@ msgstr "" "确定使用三元图相似性匹配时搜索的模糊程度(例如,较低的值意味着忽略更多的打字" "错误)。" -#: .\cookbook\forms.py:443 +#: .\cookbook\forms.py:461 msgid "" "Select type method of search. Click here for " "full description of choices." @@ -262,31 +262,31 @@ msgstr "" "选择搜索类型方法。 点击此处 查看选项的完整说" "明。" -#: .\cookbook\forms.py:444 +#: .\cookbook\forms.py:462 msgid "" "Use fuzzy matching on units, keywords and ingredients when editing and " "importing recipes." msgstr "编辑和导入菜谱时,对单位、关键词和食材使用模糊匹配。" -#: .\cookbook\forms.py:446 +#: .\cookbook\forms.py:464 msgid "" "Fields to search ignoring accents. Selecting this option can improve or " "degrade search quality depending on language" msgstr "忽略搜索字段的重音。此选项会因语言差异导致搜索质量产生变化" -#: .\cookbook\forms.py:448 +#: .\cookbook\forms.py:466 msgid "" "Fields to search for partial matches. (e.g. searching for 'Pie' will return " "'pie' and 'piece' and 'soapie')" msgstr "用于搜索部分匹配的字段。(如搜索“Pie”会返回“pie”、“piece”和“soapie”)" -#: .\cookbook\forms.py:450 +#: .\cookbook\forms.py:468 msgid "" "Fields to search for beginning of word matches. (e.g. searching for 'sa' " "will return 'salad' and 'sandwich')" msgstr "用于搜索开头匹配的字段。(如搜索“sa”会返回“salad”和“sandwich”)" -#: .\cookbook\forms.py:452 +#: .\cookbook\forms.py:470 msgid "" "Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) " "Note: this option will conflict with 'web' and 'raw' methods of search." @@ -294,41 +294,41 @@ msgstr "" "“模糊”搜索字段。(例如搜索“recpie”将会找到“recipe”。)注意:此选项将" "与“web”和“raw”搜索方法冲突。" -#: .\cookbook\forms.py:454 +#: .\cookbook\forms.py:472 msgid "" "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods " "only function with fulltext fields." msgstr "全文搜索字段。“web”、“phrase”和“raw”搜索方法仅适用于全文字段。" -#: .\cookbook\forms.py:458 +#: .\cookbook\forms.py:476 msgid "Search Method" msgstr "搜索方法" -#: .\cookbook\forms.py:459 +#: .\cookbook\forms.py:477 msgid "Fuzzy Lookups" msgstr "模糊查找" -#: .\cookbook\forms.py:460 +#: .\cookbook\forms.py:478 msgid "Ignore Accent" msgstr "忽略重音" -#: .\cookbook\forms.py:461 +#: .\cookbook\forms.py:479 msgid "Partial Match" msgstr "部分匹配" -#: .\cookbook\forms.py:462 +#: .\cookbook\forms.py:480 msgid "Starts With" msgstr "起始于" -#: .\cookbook\forms.py:463 +#: .\cookbook\forms.py:481 msgid "Fuzzy Search" msgstr "模糊搜索" -#: .\cookbook\forms.py:464 +#: .\cookbook\forms.py:482 msgid "Full Text" msgstr "全文" -#: .\cookbook\forms.py:489 +#: .\cookbook\forms.py:507 msgid "" "Users will see all items you add to your shopping list. They must add you " "to see items on their list." @@ -336,103 +336,103 @@ msgstr "" "用户将看到你添加到购物清单中的所有商品。他们必须将你添加到列表才能看到他们清" "单上的项目。" -#: .\cookbook\forms.py:495 +#: .\cookbook\forms.py:513 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "include all related recipes." msgstr "将膳食计划(手动或自动)添加到购物清单时,包括所有相关食谱。" -#: .\cookbook\forms.py:496 +#: .\cookbook\forms.py:514 msgid "" "When adding a meal plan to the shopping list (manually or automatically), " "exclude ingredients that are on hand." msgstr "将膳食计划(手动或自动)添加到购物清单时,排除现有食材。" -#: .\cookbook\forms.py:497 +#: .\cookbook\forms.py:515 msgid "Default number of hours to delay a shopping list entry." msgstr "延迟购物清单条目的默认小时数。" -#: .\cookbook\forms.py:498 +#: .\cookbook\forms.py:516 msgid "Filter shopping list to only include supermarket categories." msgstr "筛选购物清单仅包含超市分类。" -#: .\cookbook\forms.py:499 +#: .\cookbook\forms.py:517 msgid "Days of recent shopping list entries to display." msgstr "显示最近几天的购物清单列表。" -#: .\cookbook\forms.py:500 +#: .\cookbook\forms.py:518 msgid "Mark food 'On Hand' when checked off shopping list." msgstr "在核对购物清单时,将食物标记为“入手”。" -#: .\cookbook\forms.py:501 +#: .\cookbook\forms.py:519 msgid "Delimiter to use for CSV exports." msgstr "用于 CSV 导出的分隔符。" -#: .\cookbook\forms.py:502 +#: .\cookbook\forms.py:520 msgid "Prefix to add when copying list to the clipboard." msgstr "将清单复制到剪贴板时要添加的前缀。" -#: .\cookbook\forms.py:506 +#: .\cookbook\forms.py:524 msgid "Share Shopping List" msgstr "分享购物清单" -#: .\cookbook\forms.py:507 +#: .\cookbook\forms.py:525 msgid "Autosync" msgstr "自动同步" -#: .\cookbook\forms.py:508 +#: .\cookbook\forms.py:526 msgid "Auto Add Meal Plan" msgstr "自动添加膳食计划" -#: .\cookbook\forms.py:509 +#: .\cookbook\forms.py:527 msgid "Exclude On Hand" msgstr "排除现有" -#: .\cookbook\forms.py:510 +#: .\cookbook\forms.py:528 msgid "Include Related" msgstr "包括相关" -#: .\cookbook\forms.py:511 +#: .\cookbook\forms.py:529 msgid "Default Delay Hours" msgstr "默认延迟时间" -#: .\cookbook\forms.py:512 +#: .\cookbook\forms.py:530 msgid "Filter to Supermarket" msgstr "按超市筛选" -#: .\cookbook\forms.py:513 +#: .\cookbook\forms.py:531 msgid "Recent Days" msgstr "最近几天" -#: .\cookbook\forms.py:514 +#: .\cookbook\forms.py:532 msgid "CSV Delimiter" msgstr "CSV 分隔符" -#: .\cookbook\forms.py:515 +#: .\cookbook\forms.py:533 msgid "List Prefix" msgstr "清单前缀" -#: .\cookbook\forms.py:516 +#: .\cookbook\forms.py:534 msgid "Auto On Hand" msgstr "自动入手" -#: .\cookbook\forms.py:526 +#: .\cookbook\forms.py:544 msgid "Reset Food Inheritance" msgstr "重置食物材料" -#: .\cookbook\forms.py:527 +#: .\cookbook\forms.py:545 msgid "Reset all food to inherit the fields configured." msgstr "重置所有食物以继承配置的字段。" -#: .\cookbook\forms.py:539 +#: .\cookbook\forms.py:557 msgid "Fields on food that should be inherited by default." msgstr "默认情况下应继承的食物上的字段。" -#: .\cookbook\forms.py:540 +#: .\cookbook\forms.py:558 msgid "Show recipe counts on search filters" msgstr "显示搜索筛选器上的食谱计数" -#: .\cookbook\forms.py:541 +#: .\cookbook\forms.py:559 msgid "Use the plural form for units and food inside this space." msgstr "在此空间内使用复数形式表示单位和食物。" @@ -443,7 +443,7 @@ msgid "" msgstr "为了防止垃圾邮件,所要求的电子邮件没有被发送。请等待几分钟后再试。" #: .\cookbook\helper\permission_helper.py:164 -#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:114 +#: .\cookbook\helper\permission_helper.py:187 .\cookbook\views\views.py:117 msgid "You are not logged in and therefore cannot view this page!" msgstr "你没有登录,因此不能查看这个页面!" @@ -456,7 +456,7 @@ msgstr "你没有登录,因此不能查看这个页面!" #: .\cookbook\helper\permission_helper.py:305 #: .\cookbook\helper\permission_helper.py:321 #: .\cookbook\helper\permission_helper.py:342 .\cookbook\views\data.py:36 -#: .\cookbook\views\views.py:125 .\cookbook\views\views.py:132 +#: .\cookbook\views\views.py:128 .\cookbook\views\views.py:135 msgid "You do not have the required permissions to view this page!" msgstr "你没有必要的权限来查看这个页面!" @@ -475,11 +475,41 @@ msgstr "你已经达到了空间的菜谱的最大数量。" msgid "You have more users than allowed in your space." msgstr "你的空间中的用户数超过了允许的数量。" -#: .\cookbook\helper\recipe_search.py:570 +#: .\cookbook\helper\recipe_search.py:630 msgid "One of queryset or hash_key must be provided" msgstr "必须提供 queryset 或 hash_key 之一" -#: .\cookbook\helper\shopping_helper.py:152 +#: .\cookbook\helper\recipe_url_import.py:266 +#, fuzzy +#| msgid "Use fractions" +msgid "reverse rotation" +msgstr "使用分数" + +#: .\cookbook\helper\recipe_url_import.py:267 +msgid "careful rotation" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:268 +msgid "knead" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:269 +msgid "thicken" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:270 +msgid "warm up" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:271 +msgid "ferment" +msgstr "" + +#: .\cookbook\helper\recipe_url_import.py:272 +msgid "sous-vide" +msgstr "" + +#: .\cookbook\helper\shopping_helper.py:157 msgid "You must supply a servings size" msgstr "你必须提供一些份量" @@ -497,36 +527,42 @@ msgstr "喜欢" msgid "I made this" msgstr "我做的" -#: .\cookbook\integration\integration.py:223 +#: .\cookbook\integration\integration.py:218 msgid "" "Importer expected a .zip file. Did you choose the correct importer type for " "your data ?" msgstr "需要一个 .zip 文件。你是否为数据选择了正确的导入器类型?" -#: .\cookbook\integration\integration.py:226 +#: .\cookbook\integration\integration.py:221 msgid "" "An unexpected error occurred during the import. Please make sure you have " "uploaded a valid file." msgstr "在导入过程中发生了一个意外的错误。请确认你已经上传了一个有效的文件。" -#: .\cookbook\integration\integration.py:231 +#: .\cookbook\integration\integration.py:226 msgid "The following recipes were ignored because they already existed:" msgstr "以下菜谱被忽略了,因为它们已经存在了:" -#: .\cookbook\integration\integration.py:235 +#: .\cookbook\integration\integration.py:230 #, python-format msgid "Imported %s recipes." msgstr "导入了%s菜谱。" -#: .\cookbook\integration\paprika.py:46 +#: .\cookbook\integration\openeats.py:26 +#, fuzzy +#| msgid "Recipe Home" +msgid "Recipe source:" +msgstr "菜谱主页" + +#: .\cookbook\integration\paprika.py:49 msgid "Notes" msgstr "说明" -#: .\cookbook\integration\paprika.py:49 +#: .\cookbook\integration\paprika.py:52 msgid "Nutritional Information" msgstr "营养信息" -#: .\cookbook\integration\paprika.py:53 +#: .\cookbook\integration\paprika.py:56 msgid "Source" msgstr "来源" @@ -593,72 +629,72 @@ msgid "" "upload." msgstr "空间的最大文件存储量,单位为 MB。0表示无限制,-1表示禁止上传文件。" -#: .\cookbook\models.py:364 .\cookbook\templates\search.html:7 +#: .\cookbook\models.py:365 .\cookbook\templates\search.html:7 #: .\cookbook\templates\settings.html:18 #: .\cookbook\templates\space_manage.html:7 msgid "Search" msgstr "搜索" -#: .\cookbook\models.py:365 .\cookbook\templates\base.html:110 +#: .\cookbook\models.py:366 .\cookbook\templates\base.html:110 #: .\cookbook\templates\meal_plan.html:7 .\cookbook\views\delete.py:178 #: .\cookbook\views\edit.py:211 .\cookbook\views\new.py:179 msgid "Meal-Plan" msgstr "膳食计划" -#: .\cookbook\models.py:366 .\cookbook\templates\base.html:118 +#: .\cookbook\models.py:367 .\cookbook\templates\base.html:118 msgid "Books" msgstr "书籍" -#: .\cookbook\models.py:579 +#: .\cookbook\models.py:580 msgid " is part of a recipe step and cannot be deleted" msgstr " 是菜谱步骤的一部分,不能删除" -#: .\cookbook\models.py:1180 .\cookbook\templates\search_info.html:28 +#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:28 msgid "Simple" msgstr "简明" -#: .\cookbook\models.py:1181 .\cookbook\templates\search_info.html:33 +#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:33 msgid "Phrase" msgstr "短语" -#: .\cookbook\models.py:1182 .\cookbook\templates\search_info.html:38 +#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:38 msgid "Web" msgstr "网络" -#: .\cookbook\models.py:1183 .\cookbook\templates\search_info.html:47 +#: .\cookbook\models.py:1184 .\cookbook\templates\search_info.html:47 msgid "Raw" msgstr "原始" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Food Alias" msgstr "食物别名" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Unit Alias" msgstr "单位别名" -#: .\cookbook\models.py:1230 +#: .\cookbook\models.py:1231 msgid "Keyword Alias" msgstr "关键词别名" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Description Replace" msgstr "描述" -#: .\cookbook\models.py:1231 +#: .\cookbook\models.py:1232 msgid "Instruction Replace" msgstr "指示" -#: .\cookbook\models.py:1257 .\cookbook\views\delete.py:36 +#: .\cookbook\models.py:1258 .\cookbook\views\delete.py:36 #: .\cookbook\views\edit.py:251 .\cookbook\views\new.py:48 msgid "Recipe" msgstr "菜谱" -#: .\cookbook\models.py:1258 +#: .\cookbook\models.py:1259 msgid "Food" msgstr "食物" -#: .\cookbook\models.py:1259 .\cookbook\templates\base.html:141 +#: .\cookbook\models.py:1260 .\cookbook\templates\base.html:141 msgid "Keyword" msgstr "关键词" @@ -674,64 +710,64 @@ msgstr "你已达到文件上传的限制。" msgid "Cannot modify Space owner permission." msgstr "无法修改空间所有者权限。" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "Hello" msgstr "你好" -#: .\cookbook\serializer.py:1085 +#: .\cookbook\serializer.py:1093 msgid "You have been invited by " msgstr "您已被邀请至 " -#: .\cookbook\serializer.py:1086 +#: .\cookbook\serializer.py:1094 msgid " to join their Tandoor Recipes space " msgstr " 加入他们的泥炉食谱空间 " -#: .\cookbook\serializer.py:1087 +#: .\cookbook\serializer.py:1095 msgid "Click the following link to activate your account: " msgstr "点击以下链接激活您的帐户: " -#: .\cookbook\serializer.py:1088 +#: .\cookbook\serializer.py:1096 msgid "" "If the link does not work use the following code to manually join the space: " msgstr "如果链接不起作用,请使用下面的代码手动加入空间: " -#: .\cookbook\serializer.py:1089 +#: .\cookbook\serializer.py:1097 msgid "The invitation is valid until " msgstr "邀请有效期至 " -#: .\cookbook\serializer.py:1090 +#: .\cookbook\serializer.py:1098 msgid "" "Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub " msgstr "泥炉食谱是一个开源食谱管理器。 在 GitHub 上查看 " -#: .\cookbook\serializer.py:1093 +#: .\cookbook\serializer.py:1101 msgid "Tandoor Recipes Invite" msgstr "泥炉食谱邀请" -#: .\cookbook\serializer.py:1234 +#: .\cookbook\serializer.py:1242 msgid "Existing shopping list to update" msgstr "要更新现有的购物清单" -#: .\cookbook\serializer.py:1236 +#: .\cookbook\serializer.py:1244 msgid "" "List of ingredient IDs from the recipe to add, if not provided all " "ingredients will be added." msgstr "要添加的食谱中食材识别符列表,不提供则添加所有食材。" -#: .\cookbook\serializer.py:1238 +#: .\cookbook\serializer.py:1246 msgid "" "Providing a list_recipe ID and servings of 0 will delete that shopping list." msgstr "提供一个菜谱列表识别符或份数为0将删除该购物清单。" -#: .\cookbook\serializer.py:1247 +#: .\cookbook\serializer.py:1255 msgid "Amount of food to add to the shopping list" msgstr "要添加到购物清单中的食物数量" -#: .\cookbook\serializer.py:1249 +#: .\cookbook\serializer.py:1257 msgid "ID of unit to use for the shopping list" msgstr "用于购物清单的单位识别符" -#: .\cookbook\serializer.py:1251 +#: .\cookbook\serializer.py:1259 msgid "When set to true will delete all food from active shopping lists." msgstr "当设置为 true 时,将从活动的购物列表中删除所有食物。" @@ -1553,11 +1589,11 @@ msgstr "返回" msgid "Profile" msgstr "简介" -#: .\cookbook\templates\recipe_view.html:26 +#: .\cookbook\templates\recipe_view.html:41 msgid "by" msgstr "评论者" -#: .\cookbook\templates\recipe_view.html:44 .\cookbook\views\delete.py:144 +#: .\cookbook\templates\recipe_view.html:59 .\cookbook\views\delete.py:144 #: .\cookbook\views\edit.py:171 msgid "Comment" msgstr "评论" @@ -2155,217 +2191,217 @@ msgstr "" msgid "URL Import" msgstr "链接导入" -#: .\cookbook\views\api.py:109 .\cookbook\views\api.py:201 +#: .\cookbook\views\api.py:110 .\cookbook\views\api.py:202 msgid "Parameter updated_at incorrectly formatted" msgstr "参数 updated_at 格式不正确" -#: .\cookbook\views\api.py:221 .\cookbook\views\api.py:324 +#: .\cookbook\views\api.py:222 .\cookbook\views\api.py:325 #, python-brace-format msgid "No {self.basename} with id {pk} exists" msgstr "不存在ID是 {pk} 的 {self.basename}" -#: .\cookbook\views\api.py:225 +#: .\cookbook\views\api.py:226 msgid "Cannot merge with the same object!" msgstr "无法与同一对象合并!" -#: .\cookbook\views\api.py:232 +#: .\cookbook\views\api.py:233 #, python-brace-format msgid "No {self.basename} with id {target} exists" msgstr "不存在 ID 为 {target} 的 {self.basename}" -#: .\cookbook\views\api.py:237 +#: .\cookbook\views\api.py:238 msgid "Cannot merge with child object!" msgstr "无法与子对象合并!" -#: .\cookbook\views\api.py:270 +#: .\cookbook\views\api.py:271 #, python-brace-format msgid "{source.name} was merged successfully with {target.name}" msgstr "{source.name} 已成功与 {target.name} 合并" -#: .\cookbook\views\api.py:275 +#: .\cookbook\views\api.py:276 #, python-brace-format msgid "An error occurred attempting to merge {source.name} with {target.name}" msgstr "视图合并 {source.name} 和 {target.name} 时出错" -#: .\cookbook\views\api.py:333 +#: .\cookbook\views\api.py:334 #, python-brace-format msgid "{child.name} was moved successfully to the root." msgstr "{child.name} 已成功移动到根目录。" -#: .\cookbook\views\api.py:336 .\cookbook\views\api.py:354 +#: .\cookbook\views\api.py:337 .\cookbook\views\api.py:355 msgid "An error occurred attempting to move " msgstr "尝试移动时出错 " -#: .\cookbook\views\api.py:339 +#: .\cookbook\views\api.py:340 msgid "Cannot move an object to itself!" msgstr "无法将对象移动到自身!" -#: .\cookbook\views\api.py:345 +#: .\cookbook\views\api.py:346 #, python-brace-format msgid "No {self.basename} with id {parent} exists" msgstr "不存在 ID 为 {parent} 的 {self.basename}" -#: .\cookbook\views\api.py:351 +#: .\cookbook\views\api.py:352 #, python-brace-format msgid "{child.name} was moved successfully to parent {parent.name}" msgstr "{child.name} 成功移动到父节点 {parent.name}" -#: .\cookbook\views\api.py:547 +#: .\cookbook\views\api.py:553 #, python-brace-format msgid "{obj.name} was removed from the shopping list." msgstr "{obj.name} 已从购物清单中删除。" -#: .\cookbook\views\api.py:552 .\cookbook\views\api.py:882 -#: .\cookbook\views\api.py:895 +#: .\cookbook\views\api.py:558 .\cookbook\views\api.py:888 +#: .\cookbook\views\api.py:901 #, python-brace-format msgid "{obj.name} was added to the shopping list." msgstr "{obj.name} 已添加到购物清单中。" -#: .\cookbook\views\api.py:679 +#: .\cookbook\views\api.py:685 msgid "ID of recipe a step is part of. For multiple repeat parameter." msgstr "食谱中的步骤ID。 对于多个重复参数。" -#: .\cookbook\views\api.py:681 +#: .\cookbook\views\api.py:687 msgid "Query string matched (fuzzy) against object name." msgstr "请求参数与对象名称匹配(模糊)。" -#: .\cookbook\views\api.py:725 +#: .\cookbook\views\api.py:731 msgid "" "Query string matched (fuzzy) against recipe name. In the future also " "fulltext search." msgstr "请求参数与食谱名称匹配(模糊)。 未来会添加全文搜索。" -#: .\cookbook\views\api.py:727 +#: .\cookbook\views\api.py:733 msgid "" "ID of keyword a recipe should have. For multiple repeat parameter. " "Equivalent to keywords_or" msgstr "菜谱应包含的关键字 ID。 对于多个重复参数。 相当于keywords_or" -#: .\cookbook\views\api.py:730 +#: .\cookbook\views\api.py:736 msgid "" "Keyword IDs, repeat for multiple. Return recipes with any of the keywords" msgstr "允许多个关键字 ID。 返回带有任一关键字的食谱" -#: .\cookbook\views\api.py:733 +#: .\cookbook\views\api.py:739 msgid "" "Keyword IDs, repeat for multiple. Return recipes with all of the keywords." msgstr "允许多个关键字 ID。 返回带有所有关键字的食谱。" -#: .\cookbook\views\api.py:736 +#: .\cookbook\views\api.py:742 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords." msgstr "允许多个关键字 ID。 排除带有任一关键字的食谱。" -#: .\cookbook\views\api.py:739 +#: .\cookbook\views\api.py:745 msgid "" "Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords." msgstr "允许多个关键字 ID。 排除带有所有关键字的食谱。" -#: .\cookbook\views\api.py:741 +#: .\cookbook\views\api.py:747 msgid "ID of food a recipe should have. For multiple repeat parameter." msgstr "食谱中食物带有ID。并可添加多个食物。" -#: .\cookbook\views\api.py:744 +#: .\cookbook\views\api.py:750 msgid "Food IDs, repeat for multiple. Return recipes with any of the foods" msgstr "食谱中食物带有ID。并可添加多个食物" -#: .\cookbook\views\api.py:746 +#: .\cookbook\views\api.py:752 msgid "Food IDs, repeat for multiple. Return recipes with all of the foods." msgstr "食谱中食物带有ID。返回包含任何食物的食谱。" -#: .\cookbook\views\api.py:748 +#: .\cookbook\views\api.py:754 msgid "Food IDs, repeat for multiple. Exclude recipes with any of the foods." msgstr "食谱中食物带有ID。排除包含任一食物的食谱。" -#: .\cookbook\views\api.py:750 +#: .\cookbook\views\api.py:756 msgid "Food IDs, repeat for multiple. Exclude recipes with all of the foods." msgstr "食谱中食物带有ID。排除包含所有食物的食谱。" -#: .\cookbook\views\api.py:751 +#: .\cookbook\views\api.py:757 msgid "ID of unit a recipe should have." msgstr "食谱应具有单一ID。" -#: .\cookbook\views\api.py:753 +#: .\cookbook\views\api.py:759 msgid "" "Rating a recipe should have or greater. [0 - 5] Negative value filters " "rating less than." msgstr "配方的评分范围从 0 到 5。" -#: .\cookbook\views\api.py:754 +#: .\cookbook\views\api.py:760 msgid "ID of book a recipe should be in. For multiple repeat parameter." msgstr "烹饪书应该在食谱中具有ID。并且可以添加多本。" -#: .\cookbook\views\api.py:756 +#: .\cookbook\views\api.py:762 msgid "Book IDs, repeat for multiple. Return recipes with any of the books" msgstr "书的ID允许多个。返回包含任一书籍的食谱" -#: .\cookbook\views\api.py:758 +#: .\cookbook\views\api.py:764 msgid "Book IDs, repeat for multiple. Return recipes with all of the books." msgstr "书的ID允许多个。返回包含所有书籍的食谱。" -#: .\cookbook\views\api.py:760 +#: .\cookbook\views\api.py:766 msgid "Book IDs, repeat for multiple. Exclude recipes with any of the books." msgstr "书的ID允许多个。排除包含任一书籍的食谱。" -#: .\cookbook\views\api.py:762 +#: .\cookbook\views\api.py:768 msgid "Book IDs, repeat for multiple. Exclude recipes with all of the books." msgstr "书的ID允许多个。排除包含所有书籍的食谱。" -#: .\cookbook\views\api.py:764 +#: .\cookbook\views\api.py:770 msgid "If only internal recipes should be returned. [true/false]" msgstr "只返回内部食谱。 [true/false]" -#: .\cookbook\views\api.py:766 +#: .\cookbook\views\api.py:772 msgid "Returns the results in randomized order. [true/false]" msgstr "按随机排序返回结果。 [true/ false ]" -#: .\cookbook\views\api.py:768 +#: .\cookbook\views\api.py:774 msgid "Returns new results first in search results. [true/false]" msgstr "在搜索结果中首先返回新结果。 [是/]" -#: .\cookbook\views\api.py:770 +#: .\cookbook\views\api.py:776 msgid "" "Filter recipes cooked X times or more. Negative values returns cooked less " "than X times" msgstr "筛选烹饪 X 次或更多次的食谱。 负值返回烹饪少于 X 次" -#: .\cookbook\views\api.py:772 +#: .\cookbook\views\api.py:778 msgid "" "Filter recipes last cooked on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" "筛选最后烹饪在 YYYY-MM-DD 当天或之后的食谱。 前置 - 在日期或日期之前筛选。" -#: .\cookbook\views\api.py:774 +#: .\cookbook\views\api.py:780 msgid "" "Filter recipes created on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "筛选在 YYYY-MM-DD 或之后创建的食谱。 前置 - 在日期或日期之前过滤。" -#: .\cookbook\views\api.py:776 +#: .\cookbook\views\api.py:782 msgid "" "Filter recipes updated on or after YYYY-MM-DD. Prepending - filters on or " "before date." msgstr "筛选在 YYYY-MM-DD 或之后更新的食谱。 前置 - 在日期或日期之前筛选。" -#: .\cookbook\views\api.py:778 +#: .\cookbook\views\api.py:784 msgid "" "Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending - filters on " "or before date." msgstr "" "筛选最后查看时间是在 YYYY-MM-DD 或之后的食谱。 前置 - 在日期或日期之前筛选。" -#: .\cookbook\views\api.py:780 +#: .\cookbook\views\api.py:786 msgid "Filter recipes that can be made with OnHand food. [true/false]" msgstr "筛选可以直接用手制作的食谱。 [真/]" -#: .\cookbook\views\api.py:940 +#: .\cookbook\views\api.py:946 msgid "" "Returns the shopping list entry with a primary key of id. Multiple values " "allowed." msgstr "返回主键为 id 的购物清单条目。 允许多个值。" -#: .\cookbook\views\api.py:945 +#: .\cookbook\views\api.py:951 msgid "" "Filter shopping list entries on checked. [true, false, both, recent]
    - recent includes unchecked items and recently completed items." @@ -2373,44 +2409,44 @@ msgstr "" "在选中时筛选购物清单列表。 [真, 假, 两者都有, 最近]
    - 最近包括未" "选中的项目和最近完成的项目。" -#: .\cookbook\views\api.py:948 +#: .\cookbook\views\api.py:954 msgid "Returns the shopping list entries sorted by supermarket category order." msgstr "返回按超市分类排序的购物清单列表。" -#: .\cookbook\views\api.py:1160 +#: .\cookbook\views\api.py:1166 msgid "Nothing to do." msgstr "无事可做。" -#: .\cookbook\views\api.py:1180 +#: .\cookbook\views\api.py:1198 msgid "Invalid Url" msgstr "无效网址" -#: .\cookbook\views\api.py:1187 +#: .\cookbook\views\api.py:1205 msgid "Connection Refused." msgstr "连接被拒绝。" -#: .\cookbook\views\api.py:1192 +#: .\cookbook\views\api.py:1210 msgid "Bad URL Schema." msgstr "错误的 URL Schema。" -#: .\cookbook\views\api.py:1215 +#: .\cookbook\views\api.py:1233 msgid "No usable data could be found." msgstr "找不到可用的数据。" -#: .\cookbook\views\api.py:1308 .\cookbook\views\import_export.py:114 +#: .\cookbook\views\api.py:1326 .\cookbook\views\import_export.py:117 msgid "Importing is not implemented for this provider" msgstr "此提供程序未实现导入" -#: .\cookbook\views\api.py:1352 .\cookbook\views\data.py:31 +#: .\cookbook\views\api.py:1372 .\cookbook\views\data.py:31 #: .\cookbook\views\edit.py:120 .\cookbook\views\new.py:90 msgid "This feature is not yet available in the hosted version of tandoor!" msgstr "此功能在泥炉的托管版本中尚不可用!" -#: .\cookbook\views\api.py:1374 +#: .\cookbook\views\api.py:1394 msgid "Sync successful!" msgstr "同步成功!" -#: .\cookbook\views\api.py:1379 +#: .\cookbook\views\api.py:1399 msgid "Error synchronizing with Storage" msgstr "与存储同步时出错" @@ -2470,7 +2506,7 @@ msgstr "更改已保存!" msgid "Error saving changes!" msgstr "保存更改时出错!" -#: .\cookbook\views\import_export.py:101 +#: .\cookbook\views\import_export.py:104 msgid "" "The PDF Exporter is not enabled on this instance as it is still in an " "experimental state." @@ -2516,40 +2552,40 @@ msgstr "导入新菜谱!" msgid "There was an error importing this recipe!" msgstr "导入此菜谱时出错!" -#: .\cookbook\views\views.py:86 +#: .\cookbook\views\views.py:73 .\cookbook\views\views.py:191 +#: .\cookbook\views\views.py:213 .\cookbook\views\views.py:399 +msgid "This feature is not available in the demo version!" +msgstr "此功能在演示版本中不可用!" + +#: .\cookbook\views\views.py:89 msgid "" "You have successfully created your own recipe space. Start by adding some " "recipes or invite other people to join you." msgstr "你已成功创建自己的菜谱空间。 首先添加一些菜谱或邀请其他人加入。" -#: .\cookbook\views\views.py:140 +#: .\cookbook\views\views.py:143 msgid "You do not have the required permissions to perform this action!" msgstr "您没有执行此操作所需的权限!" -#: .\cookbook\views\views.py:151 +#: .\cookbook\views\views.py:154 msgid "Comment saved!" msgstr "评论已保存!" -#: .\cookbook\views\views.py:188 .\cookbook\views\views.py:210 -#: .\cookbook\views\views.py:396 -msgid "This feature is not available in the demo version!" -msgstr "此功能在演示版本中不可用!" - -#: .\cookbook\views\views.py:250 +#: .\cookbook\views\views.py:253 msgid "You must select at least one field to search!" msgstr "你必须至少选择一个字段进行搜索!" -#: .\cookbook\views\views.py:255 +#: .\cookbook\views\views.py:258 msgid "" "To use this search method you must select at least one full text search " "field!" msgstr "要使用此搜索方法,至少选择一个全文搜索字段!" -#: .\cookbook\views\views.py:259 +#: .\cookbook\views\views.py:262 msgid "Fuzzy search is not compatible with this search method!" msgstr "模糊搜索与此搜索方法不兼容!" -#: .\cookbook\views\views.py:335 +#: .\cookbook\views\views.py:338 msgid "" "The setup page can only be used to create the first user! If you have " "forgotten your superuser credentials please consult the django documentation " @@ -2558,33 +2594,33 @@ msgstr "" "设置页面只能用于创建第一个用户!如果您忘记了超级用户凭据,请参阅 Django 文" "档,了解如何重置密码。" -#: .\cookbook\views\views.py:342 +#: .\cookbook\views\views.py:345 msgid "Passwords dont match!" msgstr "密码不匹配!" -#: .\cookbook\views\views.py:350 +#: .\cookbook\views\views.py:353 msgid "User has been created, please login!" msgstr "用户已创建,请登录!" -#: .\cookbook\views\views.py:366 +#: .\cookbook\views\views.py:369 msgid "Malformed Invite Link supplied!" msgstr "提供了格式错误的邀请链接!" -#: .\cookbook\views\views.py:383 +#: .\cookbook\views\views.py:386 msgid "Successfully joined space." msgstr "成功加入空间。" -#: .\cookbook\views\views.py:389 +#: .\cookbook\views\views.py:392 msgid "Invite Link not valid or already used!" msgstr "邀请链接无效或已使用!" -#: .\cookbook\views\views.py:406 +#: .\cookbook\views\views.py:409 msgid "" "Reporting share links is not enabled for this instance. Please notify the " "page administrator to report problems." msgstr "未为此实例启用报告共享链接。请通知页面管理员报告问题。" -#: .\cookbook\views\views.py:412 +#: .\cookbook\views\views.py:415 msgid "" "Recipe sharing link has been disabled! For additional information please " "contact the page administrator." diff --git a/cookbook/locale/zh_Hant/LC_MESSAGES/django.mo b/cookbook/locale/zh_Hant/LC_MESSAGES/django.mo index cb5715cda..7517dc2c3 100644 Binary files a/cookbook/locale/zh_Hant/LC_MESSAGES/django.mo and b/cookbook/locale/zh_Hant/LC_MESSAGES/django.mo differ diff --git a/cookbook/managers.py b/cookbook/managers.py index 5bfc60e43..09f4bd58a 100644 --- a/cookbook/managers.py +++ b/cookbook/managers.py @@ -34,35 +34,14 @@ class RecipeSearchManager(models.Manager): + SearchVector(StringAgg('steps__ingredients__food__name__unaccent', delimiter=' '), weight='B', config=language) + SearchVector(StringAgg('keywords__name__unaccent', delimiter=' '), weight='B', config=language)) search_rank = SearchRank(search_vectors, search_query) - # USING TRIGRAM BREAKS WEB SEARCH - # ADDING MULTIPLE TRIGRAMS CREATES DUPLICATE RESULTS - # DISTINCT NOT COMPAITBLE WITH ANNOTATE - # trigram_name = (TrigramSimilarity('name', search_text)) - # trigram_description = (TrigramSimilarity('description', search_text)) - # trigram_food = (TrigramSimilarity('steps__ingredients__food__name', search_text)) - # trigram_keyword = (TrigramSimilarity('keywords__name', search_text)) - # adding additional trigrams created duplicates - # + TrigramSimilarity('description', search_text) - # + TrigramSimilarity('steps__ingredients__food__name', search_text) - # + TrigramSimilarity('keywords__name', search_text) + return ( self.get_queryset() .annotate( search=search_vectors, rank=search_rank, - # trigram=trigram_name+trigram_description+trigram_food+trigram_keyword - # trigram_name=trigram_name, - # trigram_description=trigram_description, - # trigram_food=trigram_food, - # trigram_keyword=trigram_keyword ) .filter( Q(search=search_query) - # | Q(trigram_name__gt=0.1) - # | Q(name__icontains=search_text) - # | Q(trigram_name__gt=0.2) - # | Q(trigram_description__gt=0.2) - # | Q(trigram_food__gt=0.2) - # | Q(trigram_keyword__gt=0.2) ) .order_by('-rank')) diff --git a/cookbook/migrations/0189_property_propertytype_unitconversion_food_fdc_id_and_more.py b/cookbook/migrations/0189_property_propertytype_unitconversion_food_fdc_id_and_more.py new file mode 100644 index 000000000..710b5b08c --- /dev/null +++ b/cookbook/migrations/0189_property_propertytype_unitconversion_food_fdc_id_and_more.py @@ -0,0 +1,163 @@ +# Generated by Django 4.1.9 on 2023-05-25 13:05 + +import cookbook.models +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django_prometheus.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cookbook', '0188_space_no_sharing_limit'), + ] + + operations = [ + migrations.CreateModel( + name='Property', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('property_amount', models.DecimalField(decimal_places=4, default=0, max_digits=32)), + ], + bases=(models.Model, cookbook.models.PermissionModelMixin), + ), + migrations.CreateModel( + name='PropertyType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128)), + ('unit', models.CharField(blank=True, max_length=64, null=True)), + ('icon', models.CharField(blank=True, max_length=16, null=True)), + ('description', models.CharField(blank=True, max_length=512, null=True)), + ('category', models.CharField(blank=True, choices=[('NUTRITION', 'Nutrition'), ('ALLERGEN', 'Allergen'), ('PRICE', 'Price'), ('GOAL', 'Goal'), ('OTHER', 'Other')], max_length=64, null=True)), + ('open_data_slug', models.CharField(blank=True, default=None, max_length=128, null=True)), + ], + bases=(models.Model, cookbook.models.PermissionModelMixin), + ), + migrations.CreateModel( + name='UnitConversion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('base_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)), + ('converted_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('open_data_slug', models.CharField(blank=True, default=None, max_length=128, null=True)), + ], + bases=(django_prometheus.models.ExportModelOperationsMixin('unit_conversion'), models.Model, cookbook.models.PermissionModelMixin), + ), + migrations.AddField( + model_name='food', + name='fdc_id', + field=models.CharField(blank=True, default=None, max_length=128, null=True), + ), + migrations.AddField( + model_name='food', + name='open_data_slug', + field=models.CharField(blank=True, default=None, max_length=128, null=True), + ), + migrations.AddField( + model_name='food', + name='preferred_shopping_unit', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='preferred_shopping_unit', to='cookbook.unit'), + ), + migrations.AddField( + model_name='food', + name='preferred_unit', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='preferred_unit', to='cookbook.unit'), + ), + migrations.AddField( + model_name='food', + name='properties_food_amount', + field=models.IntegerField(blank=True, default=100), + ), + migrations.AddField( + model_name='food', + name='properties_food_unit', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cookbook.unit'), + ), + migrations.AddField( + model_name='supermarket', + name='open_data_slug', + field=models.CharField(blank=True, default=None, max_length=128, null=True), + ), + migrations.AddField( + model_name='supermarketcategory', + name='open_data_slug', + field=models.CharField(blank=True, default=None, max_length=128, null=True), + ), + migrations.AddField( + model_name='unit', + name='base_unit', + field=models.TextField(blank=True, default=None, max_length=256, null=True), + ), + migrations.AddField( + model_name='unit', + name='open_data_slug', + field=models.CharField(blank=True, default=None, max_length=128, null=True), + ), + migrations.AddConstraint( + model_name='supermarketcategoryrelation', + constraint=models.UniqueConstraint(fields=('supermarket', 'category'), name='unique_sm_category_relation'), + ), + migrations.AddField( + model_name='unitconversion', + name='base_unit', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_base_relation', to='cookbook.unit'), + ), + migrations.AddField( + model_name='unitconversion', + name='converted_unit', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_converted_relation', to='cookbook.unit'), + ), + migrations.AddField( + model_name='unitconversion', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='unitconversion', + name='food', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cookbook.food'), + ), + migrations.AddField( + model_name='unitconversion', + name='space', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space'), + ), + migrations.AddField( + model_name='propertytype', + name='space', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space'), + ), + migrations.AddField( + model_name='property', + name='property_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.propertytype'), + ), + migrations.AddField( + model_name='property', + name='space', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space'), + ), + migrations.AddField( + model_name='food', + name='properties', + field=models.ManyToManyField(blank=True, to='cookbook.property'), + ), + migrations.AddField( + model_name='recipe', + name='properties', + field=models.ManyToManyField(blank=True, to='cookbook.property'), + ), + migrations.AddConstraint( + model_name='unitconversion', + constraint=models.UniqueConstraint(fields=('space', 'base_unit', 'converted_unit', 'food'), name='f_unique_conversion_per_space'), + ), + migrations.AddConstraint( + model_name='propertytype', + constraint=models.UniqueConstraint(fields=('space', 'name'), name='property_type_unique_name_per_space'), + ), + ] diff --git a/cookbook/migrations/0190_auto_20230525_1506.py b/cookbook/migrations/0190_auto_20230525_1506.py new file mode 100644 index 000000000..af34cdd6c --- /dev/null +++ b/cookbook/migrations/0190_auto_20230525_1506.py @@ -0,0 +1,38 @@ +# Generated by Django 4.1.9 on 2023-05-25 13:06 + +from django.db import migrations +from django_scopes import scopes_disabled +from gettext import gettext as _ + +def migrate_old_nutrition_data(apps, schema_editor): + print('Transforming nutrition information, this might take a while on large databases') + with scopes_disabled(): + PropertyType = apps.get_model('cookbook', 'PropertyType') + RecipeProperty = apps.get_model('cookbook', 'Property') + Recipe = apps.get_model('cookbook', 'Recipe') + Space = apps.get_model('cookbook', 'Space') + + # TODO respect space + for s in Space.objects.all(): + property_fat = PropertyType.objects.get_or_create(name=_('Fat'), unit=_('g'), space=s, )[0] + property_carbohydrates = PropertyType.objects.get_or_create(name=_('Carbohydrates'), unit=_('g'), space=s, )[0] + property_proteins = PropertyType.objects.get_or_create(name=_('Proteins'), unit=_('g'), space=s, )[0] + property_calories = PropertyType.objects.get_or_create(name=_('Calories'), unit=_('kcal'), space=s, )[0] + + for r in Recipe.objects.filter(nutrition__isnull=False, space=s).all(): + rp_fat = RecipeProperty.objects.create(property_type=property_fat, property_amount=r.nutrition.fats, space=s) + rp_carbohydrates = RecipeProperty.objects.create(property_type=property_carbohydrates, property_amount=r.nutrition.carbohydrates, space=s) + rp_proteins = RecipeProperty.objects.create(property_type=property_proteins, property_amount=r.nutrition.proteins, space=s) + rp_calories = RecipeProperty.objects.create(property_type=property_calories, property_amount=r.nutrition.calories, space=s) + r.properties.add(rp_fat, rp_carbohydrates, rp_proteins, rp_calories) + r.nutrition = None + r.save() +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0189_property_propertytype_unitconversion_food_fdc_id_and_more'), + ] + + operations = [ + migrations.RunPython(migrate_old_nutrition_data) + ] diff --git a/cookbook/migrations/0191_foodproperty_property_import_food_id_and_more.py b/cookbook/migrations/0191_foodproperty_property_import_food_id_and_more.py new file mode 100644 index 000000000..5b7d682bf --- /dev/null +++ b/cookbook/migrations/0191_foodproperty_property_import_food_id_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 4.1.9 on 2023-06-20 13:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('cookbook', '0190_auto_20230525_1506'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=[ + migrations.RunSQL( + sql="ALTER TABLE cookbook_food_properties RENAME TO cookbook_foodproperty", + reverse_sql="ALTER TABLE cookbook_foodproperty RENAME TO cookbook_food_properties", + ), + ], + state_operations=[ + migrations.CreateModel( + name='FoodProperty', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('food', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.food')), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.property')), + ], + ), + migrations.AlterField( + model_name='food', + name='properties', + field=models.ManyToManyField(blank=True, through='cookbook.FoodProperty', to='cookbook.property'), + ), + ] + ), + migrations.AddConstraint( + model_name='foodproperty', + constraint=models.UniqueConstraint(fields=('food', 'property'), name='property_unique_food'), + ), + migrations.AddField( + model_name='property', + name='import_food_id', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddConstraint( + model_name='property', + constraint=models.UniqueConstraint(fields=('space', 'property_type', 'import_food_id'), name='property_unique_import_food_per_space'), + ), + ] diff --git a/cookbook/migrations/0192_food_food_unique_open_data_slug_per_space_and_more.py b/cookbook/migrations/0192_food_food_unique_open_data_slug_per_space_and_more.py new file mode 100644 index 000000000..09865486c --- /dev/null +++ b/cookbook/migrations/0192_food_food_unique_open_data_slug_per_space_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 4.1.9 on 2023-06-20 13:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0191_foodproperty_property_import_food_id_and_more'), + ] + + operations = [ + migrations.AddConstraint( + model_name='food', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='food_unique_open_data_slug_per_space'), + ), + migrations.AddConstraint( + model_name='propertytype', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='property_type_unique_open_data_slug_per_space'), + ), + migrations.AddConstraint( + model_name='supermarket', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='supermarket_unique_open_data_slug_per_space'), + ), + migrations.AddConstraint( + model_name='supermarketcategory', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='supermarket_category_unique_open_data_slug_per_space'), + ), + migrations.AddConstraint( + model_name='unit', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='unit_unique_open_data_slug_per_space'), + ), + migrations.AddConstraint( + model_name='unitconversion', + constraint=models.UniqueConstraint(fields=('space', 'open_data_slug'), name='unit_conversion_unique_open_data_slug_per_space'), + ), + ] diff --git a/cookbook/migrations/0193_space_internal_note.py b/cookbook/migrations/0193_space_internal_note.py new file mode 100644 index 000000000..d29d4f75d --- /dev/null +++ b/cookbook/migrations/0193_space_internal_note.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.9 on 2023-06-21 13:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0192_food_food_unique_open_data_slug_per_space_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='space', + name='internal_note', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/cookbook/migrations/0194_alter_food_properties_food_amount.py b/cookbook/migrations/0194_alter_food_properties_food_amount.py new file mode 100644 index 000000000..b1d309590 --- /dev/null +++ b/cookbook/migrations/0194_alter_food_properties_food_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.9 on 2023-06-26 13:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0193_space_internal_note'), + ] + + operations = [ + migrations.AlterField( + model_name='food', + name='properties_food_amount', + field=models.DecimalField(blank=True, decimal_places=2, default=100, max_digits=16), + ), + ] diff --git a/cookbook/migrations/0195_invitelink_internal_note_userspace_internal_note_and_more.py b/cookbook/migrations/0195_invitelink_internal_note_userspace_internal_note_and_more.py new file mode 100644 index 000000000..01562b85f --- /dev/null +++ b/cookbook/migrations/0195_invitelink_internal_note_userspace_internal_note_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 4.1.9 on 2023-06-30 20:34 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0194_alter_food_properties_food_amount'), + ] + + operations = [ + migrations.AddField( + model_name='invitelink', + name='internal_note', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='userspace', + name='internal_note', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='userspace', + name='invite_link', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cookbook.invitelink'), + ), + migrations.AlterField( + model_name='userpreference', + name='theme', + field=models.CharField(choices=[('TANDOOR', 'Tandoor'), ('BOOTSTRAP', 'Bootstrap'), ('DARKLY', 'Darkly'), ('FLATLY', 'Flatly'), ('SUPERHERO', 'Superhero'), ('TANDOOR_DARK', 'Tandoor Dark (INCOMPLETE)')], default='TANDOOR', max_length=128), + ), + ] diff --git a/cookbook/migrations/0196_food_url.py b/cookbook/migrations/0196_food_url.py new file mode 100644 index 000000000..aef8c5953 --- /dev/null +++ b/cookbook/migrations/0196_food_url.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-07-22 06:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0195_invitelink_internal_note_userspace_internal_note_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='food', + name='url', + field=models.CharField(blank=True, default='', max_length=1024, null=True), + ), + ] diff --git a/cookbook/migrations/0197_step_show_ingredients_table_and_more.py b/cookbook/migrations/0197_step_show_ingredients_table_and_more.py new file mode 100644 index 000000000..47264ece8 --- /dev/null +++ b/cookbook/migrations/0197_step_show_ingredients_table_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.10 on 2023-08-24 08:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0196_food_url'), + ] + + operations = [ + migrations.AddField( + model_name='step', + name='show_ingredients_table', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='userpreference', + name='show_step_ingredients', + field=models.BooleanField(default=True), + ), + ] diff --git a/cookbook/migrations/0198_propertytype_order.py b/cookbook/migrations/0198_propertytype_order.py new file mode 100644 index 000000000..bbda0dc8a --- /dev/null +++ b/cookbook/migrations/0198_propertytype_order.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-08-24 09:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0197_step_show_ingredients_table_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='propertytype', + name='order', + field=models.IntegerField(default=0), + ), + ] diff --git a/cookbook/migrations/0199_alter_propertytype_options_alter_automation_type_and_more.py b/cookbook/migrations/0199_alter_propertytype_options_alter_automation_type_and_more.py new file mode 100644 index 000000000..39734349e --- /dev/null +++ b/cookbook/migrations/0199_alter_propertytype_options_alter_automation_type_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.1.10 on 2023-09-01 17:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0198_propertytype_order'), + ] + + operations = [ + migrations.AlterField( + model_name='automation', + name='type', + field=models.CharField( + choices=[ + ('FOOD_ALIAS', 'Food Alias'), + ('UNIT_ALIAS', 'Unit Alias'), + ('KEYWORD_ALIAS', 'Keyword Alias'), + ('DESCRIPTION_REPLACE', 'Description Replace'), + ('INSTRUCTION_REPLACE', 'Instruction Replace'), + ('NEVER_UNIT', 'Never Unit'), + ('TRANSPOSE_WORDS', 'Transpose Words'), + ('FOOD_REPLACE', 'Food Replace'), + ('UNIT_REPLACE', 'Unit Replace'), + ('NAME_REPLACE', 'Name Replace')], + max_length=128), + ), + ] diff --git a/cookbook/migrations/0200_alter_propertytype_options_remove_keyword_icon_and_more.py b/cookbook/migrations/0200_alter_propertytype_options_remove_keyword_icon_and_more.py new file mode 100644 index 000000000..104d6a721 --- /dev/null +++ b/cookbook/migrations/0200_alter_propertytype_options_remove_keyword_icon_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 4.1.10 on 2023-08-29 11:59 + +from django.db import migrations +from django.db.models import F, Value +from django.db.models.functions import Concat +from django_scopes import scopes_disabled + + +def migrate_icons(apps, schema_editor): + with scopes_disabled(): + MealType = apps.get_model('cookbook', 'MealType') + Keyword = apps.get_model('cookbook', 'Keyword') + PropertyType = apps.get_model('cookbook', 'PropertyType') + RecipeBook = apps.get_model('cookbook', 'RecipeBook') + + MealType.objects.update(name=Concat(F('icon'), Value(' '), F('name'))) + Keyword.objects.update(name=Concat(F('icon'), Value(' '), F('name'))) + PropertyType.objects.update(name=Concat(F('icon'), Value(' '), F('name'))) + RecipeBook.objects.update(name=Concat(F('icon'), Value(' '), F('name'))) + + +class Migration(migrations.Migration): + dependencies = [ + ('cookbook', '0199_alter_propertytype_options_alter_automation_type_and_more'), + ] + + operations = [ + + migrations.RunPython( + migrate_icons + ), + migrations.AlterModelOptions( + name='propertytype', + options={'ordering': ('order',)}, + ), + migrations.RemoveField( + model_name='keyword', + name='icon', + ), + migrations.RemoveField( + model_name='mealtype', + name='icon', + ), + migrations.RemoveField( + model_name='propertytype', + name='icon', + ), + migrations.RemoveField( + model_name='recipebook', + name='icon', + ), + ] diff --git a/cookbook/migrations/0201_rename_date_mealplan_from_date_mealplan_to_date.py b/cookbook/migrations/0201_rename_date_mealplan_from_date_mealplan_to_date.py new file mode 100644 index 000000000..343d758ed --- /dev/null +++ b/cookbook/migrations/0201_rename_date_mealplan_from_date_mealplan_to_date.py @@ -0,0 +1,36 @@ +# Generated by Django 4.1.10 on 2023-09-08 12:20 + +from django.db import migrations, models +from django.db.models import F +from django_scopes import scopes_disabled + + +def apply_migration(apps, schema_editor): + with scopes_disabled(): + MealPlan = apps.get_model('cookbook', 'MealPlan') + MealPlan.objects.update(to_date=F('from_date')) + + +class Migration(migrations.Migration): + dependencies = [ + ('cookbook', '0200_alter_propertytype_options_remove_keyword_icon_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='mealplan', + old_name='date', + new_name='from_date', + ), + migrations.AddField( + model_name='mealplan', + name='to_date', + field=models.DateField(blank=True, null=True), + ), + migrations.RunPython(apply_migration), + migrations.AlterField( + model_name='mealplan', + name='to_date', + field=models.DateField(), + ), + ] diff --git a/cookbook/migrations/0202_remove_space_show_facet_count.py b/cookbook/migrations/0202_remove_space_show_facet_count.py new file mode 100644 index 000000000..147c718dd --- /dev/null +++ b/cookbook/migrations/0202_remove_space_show_facet_count.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.10 on 2023-09-12 13:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0201_rename_date_mealplan_from_date_mealplan_to_date'), + ] + + operations = [ + migrations.RemoveField( + model_name='space', + name='show_facet_count', + ), + ] diff --git a/cookbook/migrations/0203_alter_unique_contstraints.py b/cookbook/migrations/0203_alter_unique_contstraints.py new file mode 100644 index 000000000..7dc6cc08c --- /dev/null +++ b/cookbook/migrations/0203_alter_unique_contstraints.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2023-09-14 12:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0202_remove_space_show_facet_count'), + ] + + operations = [ + migrations.AddConstraint( + model_name='mealtype', + constraint=models.UniqueConstraint(fields=('space', 'name'), name='mt_unique_name_per_space'), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 1732750b9..caf84d790 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -5,7 +5,6 @@ import uuid from datetime import date, timedelta import oauth2_provider.models -from PIL import Image from annoying.fields import AutoOneToOneField from django.contrib import auth from django.contrib.auth.models import Group, User @@ -14,13 +13,14 @@ from django.contrib.postgres.search import SearchVectorField from django.core.files.uploadedfile import InMemoryUploadedFile, UploadedFile from django.core.validators import MinLengthValidator from django.db import IntegrityError, models -from django.db.models import Index, ProtectedError, Q, Avg, Max +from django.db.models import Avg, Index, Max, ProtectedError, Q from django.db.models.fields.related import ManyToManyField from django.db.models.functions import Substr from django.utils import timezone from django.utils.translation import gettext as _ from django_prometheus.models import ExportModelOperationsMixin from django_scopes import ScopedManager, scopes_disabled +from PIL import Image from treebeard.mp_tree import MP_Node, MP_NodeManager from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PREF_DEFAULT, @@ -82,41 +82,41 @@ class TreeManager(MP_NodeManager): # model.Manager get_or_create() is not compatible with MP_Tree def get_or_create(self, *args, **kwargs): kwargs['name'] = kwargs['name'].strip() - - if obj := self.filter(name__iexact=kwargs['name'], space=kwargs['space']).first(): - return obj, False + if hasattr(self, 'space'): + if obj := self.filter(name__iexact=kwargs['name'], space=kwargs['space']).first(): + return obj, False else: - with scopes_disabled(): - try: - defaults = kwargs.pop('defaults', None) - if defaults: - kwargs = {**kwargs, **defaults} - # ManyToMany fields can't be set this way, so pop them out to save for later - fields = [field.name for field in self.model._meta.get_fields() if issubclass(type(field), ManyToManyField)] - many_to_many = {field: kwargs.pop(field) for field in list(kwargs) if field in fields} - obj = self.model.add_root(**kwargs) - for field in many_to_many: - field_model = getattr(obj, field).model - for related_obj in many_to_many[field]: - if isinstance(related_obj, User): - getattr(obj, field).add(field_model.objects.get(id=related_obj.id)) - else: - getattr(obj, field).add(field_model.objects.get(**dict(related_obj))) - return obj, True - except IntegrityError as e: - if 'Key (path)' in e.args[0]: - self.model.fix_tree(fix_paths=True) - return self.model.add_root(**kwargs), True + if obj := self.filter(name__iexact=kwargs['name']).first(): + return obj, False + + with scopes_disabled(): + try: + defaults = kwargs.pop('defaults', None) + if defaults: + kwargs = {**kwargs, **defaults} + # ManyToMany fields can't be set this way, so pop them out to save for later + fields = [field.name for field in self.model._meta.get_fields() if issubclass(type(field), ManyToManyField)] + many_to_many = {field: kwargs.pop(field) for field in list(kwargs) if field in fields} + obj = self.model.add_root(**kwargs) + for field in many_to_many: + field_model = getattr(obj, field).model + for related_obj in many_to_many[field]: + if isinstance(related_obj, User): + getattr(obj, field).add(field_model.objects.get(id=related_obj.id)) + else: + getattr(obj, field).add(field_model.objects.get(**dict(related_obj))) + return obj, True + except IntegrityError as e: + if 'Key (path)' in e.args[0]: + self.model.fix_tree(fix_paths=True) + return self.model.add_root(**kwargs), True class TreeModel(MP_Node): _full_name_separator = ' > ' def __str__(self): - if self.icon: - return f"{self.icon} {self.name}" - else: - return f"{self.name}" + return f"{self.name}" @property def parent(self): @@ -185,7 +185,6 @@ class TreeModel(MP_Node): :param filter: Filter (include) the descendants nodes with the provided Q filter """ descendants = Q() - # TODO filter the queryset nodes to exclude descendants of objects in the queryset nodes = queryset.values('path', 'depth') for node in nodes: descendants |= Q(path__startswith=node['path'], depth__gt=node['depth']) @@ -265,7 +264,8 @@ class Space(ExportModelOperationsMixin('space'), models.Model): no_sharing_limit = models.BooleanField(default=False) demo = models.BooleanField(default=False) food_inherit = models.ManyToManyField(FoodInheritField, blank=True) - show_facet_count = models.BooleanField(default=False) + + internal_note = models.TextField(blank=True, null=True) def safe_delete(self): """ @@ -326,6 +326,7 @@ class UserPreference(models.Model, PermissionModelMixin): FLATLY = 'FLATLY' SUPERHERO = 'SUPERHERO' TANDOOR = 'TANDOOR' + TANDOOR_DARK = 'TANDOOR_DARK' THEMES = ( (TANDOOR, 'Tandoor'), @@ -333,6 +334,7 @@ class UserPreference(models.Model, PermissionModelMixin): (DARKLY, 'Darkly'), (FLATLY, 'Flatly'), (SUPERHERO, 'Superhero'), + (TANDOOR_DARK, 'Tandoor Dark (INCOMPLETE)'), ) # Nav colors @@ -387,6 +389,7 @@ class UserPreference(models.Model, PermissionModelMixin): shopping_add_onhand = models.BooleanField(default=False) filter_to_supermarket = models.BooleanField(default=False) left_handed = models.BooleanField(default=False) + show_step_ingredients = models.BooleanField(default=True) default_delay = models.DecimalField(default=4, max_digits=8, decimal_places=4) shopping_recent_days = models.PositiveIntegerField(default=7) csv_delim = models.CharField(max_length=2, default=",") @@ -408,6 +411,9 @@ class UserSpace(models.Model, PermissionModelMixin): # that having more than one active space should just break certain parts of the application and not leak any data active = models.BooleanField(default=False) + invite_link = models.ForeignKey("InviteLink", on_delete=models.PROTECT, null=True, blank=True) + internal_note = models.TextField(blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -454,6 +460,7 @@ class Sync(models.Model, PermissionModelMixin): class SupermarketCategory(models.Model, PermissionModelMixin): name = models.CharField(max_length=128, validators=[MinLengthValidator(1)]) description = models.TextField(blank=True, null=True) + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') @@ -463,7 +470,8 @@ class SupermarketCategory(models.Model, PermissionModelMixin): class Meta: constraints = [ - models.UniqueConstraint(fields=['space', 'name'], name='smc_unique_name_per_space') + models.UniqueConstraint(fields=['space', 'name'], name='smc_unique_name_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='supermarket_category_unique_open_data_slug_per_space') ] @@ -471,6 +479,7 @@ class Supermarket(models.Model, PermissionModelMixin): name = models.CharField(max_length=128, validators=[MinLengthValidator(1)]) description = models.TextField(blank=True, null=True) categories = models.ManyToManyField(SupermarketCategory, through='SupermarketCategoryRelation') + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') @@ -480,7 +489,8 @@ class Supermarket(models.Model, PermissionModelMixin): class Meta: constraints = [ - models.UniqueConstraint(fields=['space', 'name'], name='sm_unique_name_per_space') + models.UniqueConstraint(fields=['space', 'name'], name='sm_unique_name_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='supermarket_unique_open_data_slug_per_space') ] @@ -496,6 +506,9 @@ class SupermarketCategoryRelation(models.Model, PermissionModelMixin): return 'supermarket', 'space' class Meta: + constraints = [ + models.UniqueConstraint(fields=['supermarket', 'category'], name='unique_sm_category_relation') + ] ordering = ('order',) @@ -515,7 +528,6 @@ class Keyword(ExportModelOperationsMixin('keyword'), TreeModel, PermissionModelM if SORT_TREE_BY_NAME: node_order_by = ['name'] name = models.CharField(max_length=64) - icon = models.CharField(max_length=16, blank=True, null=True) description = models.TextField(default="", blank=True) created_at = models.DateTimeField(auto_now_add=True) # TODO deprecate updated_at = models.DateTimeField(auto_now=True) # TODO deprecate @@ -534,6 +546,8 @@ class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixi name = models.CharField(max_length=128, validators=[MinLengthValidator(1)]) plural_name = models.CharField(max_length=128, null=True, blank=True, default=None) description = models.TextField(blank=True, null=True) + base_unit = models.TextField(max_length=256, null=True, blank=True, default=None) + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') @@ -543,7 +557,8 @@ class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixi class Meta: constraints = [ - models.UniqueConstraint(fields=['space', 'name'], name='u_unique_name_per_space') + models.UniqueConstraint(fields=['space', 'name'], name='u_unique_name_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='unit_unique_open_data_slug_per_space') ] @@ -559,6 +574,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): name = models.CharField(max_length=128, validators=[MinLengthValidator(1)]) plural_name = models.CharField(max_length=128, null=True, blank=True, default=None) recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL) + url = models.CharField(max_length=1024, blank=True, null=True, default='') supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL) # inherited field ignore_shopping = models.BooleanField(default=False) # inherited field onhand_users = models.ManyToManyField(User, blank=True) @@ -569,6 +585,15 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): substitute_children = models.BooleanField(default=False) child_inherit_fields = models.ManyToManyField(FoodInheritField, blank=True, related_name='child_inherit') + properties = models.ManyToManyField("Property", blank=True, through='FoodProperty') + properties_food_amount = models.DecimalField(default=100, max_digits=16, decimal_places=2, blank=True) + properties_food_unit = models.ForeignKey(Unit, on_delete=models.PROTECT, blank=True, null=True) + + preferred_unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True, default=None, related_name='preferred_unit') + preferred_shopping_unit = models.ForeignKey(Unit, on_delete=models.SET_NULL, null=True, blank=True, default=None, related_name='preferred_shopping_unit') + fdc_id = models.CharField(max_length=128, null=True, blank=True, default=None) + + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space', _manager_class=TreeManager) @@ -642,7 +667,8 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): class Meta: constraints = [ - models.UniqueConstraint(fields=['space', 'name'], name='f_unique_name_per_space') + models.UniqueConstraint(fields=['space', 'name'], name='f_unique_name_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='food_unique_open_data_slug_per_space') ] indexes = ( Index(fields=['id']), @@ -650,6 +676,32 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): ) +class UnitConversion(ExportModelOperationsMixin('unit_conversion'), models.Model, PermissionModelMixin): + base_amount = models.DecimalField(default=0, decimal_places=16, max_digits=32) + base_unit = models.ForeignKey('Unit', on_delete=models.CASCADE, related_name='unit_conversion_base_relation') + converted_amount = models.DecimalField(default=0, decimal_places=16, max_digits=32) + converted_unit = models.ForeignKey('Unit', on_delete=models.CASCADE, related_name='unit_conversion_converted_relation') + + food = models.ForeignKey('Food', on_delete=models.CASCADE, null=True, blank=True) + + created_by = models.ForeignKey(User, on_delete=models.PROTECT) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) + space = models.ForeignKey(Space, on_delete=models.CASCADE) + objects = ScopedManager(space='space') + + def __str__(self): + return f'{self.base_amount} {self.base_unit} -> {self.converted_amount} {self.converted_unit} {self.food}' + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['space', 'base_unit', 'converted_unit', 'food'], name='f_unique_conversion_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='unit_conversion_unique_open_data_slug_per_space') + ] + + class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, PermissionModelMixin): # delete method on Food and Unit checks if they are part of a Recipe, if it is raises a ProtectedError instead of cascading the delete food = models.ForeignKey(Food, on_delete=models.CASCADE, null=True, blank=True) @@ -663,30 +715,9 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss order = models.IntegerField(default=0) original_text = models.CharField(max_length=512, null=True, blank=True, default=None) - original_text = models.CharField(max_length=512, null=True, blank=True, default=None) - space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') - def __str__(self): - food = "" - unit = "" - if self.always_use_plural_food and self.food.plural_name not in (None, "") and not self.no_amount: - food = self.food.plural_name - else: - if self.amount > 1 and self.food.plural_name not in (None, "") and not self.no_amount: - food = self.food.plural_name - else: - food = str(self.food) - if self.always_use_plural_unit and self.unit.plural_name not in (None, "") and not self.no_amount: - unit = self.unit.plural_name - else: - if self.amount > 1 and self.unit is not None and self.unit.plural_name not in (None, "") and not self.no_amount: - unit = self.unit.plural_name - else: - unit = str(self.unit) - return str(self.amount) + ' ' + str(unit) + ' ' + str(food) - class Meta: ordering = ['order', 'pk'] indexes = ( @@ -702,6 +733,7 @@ class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixi order = models.IntegerField(default=0) file = models.ForeignKey('UserFile', on_delete=models.PROTECT, null=True, blank=True) show_as_header = models.BooleanField(default=True) + show_ingredients_table = models.BooleanField(default=True) search_vector = SearchVectorField(null=True) step_recipe = models.ForeignKey('Recipe', default=None, blank=True, null=True, on_delete=models.PROTECT) @@ -720,6 +752,66 @@ class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixi indexes = (GinIndex(fields=["search_vector"]),) +class PropertyType(models.Model, PermissionModelMixin): + NUTRITION = 'NUTRITION' + ALLERGEN = 'ALLERGEN' + PRICE = 'PRICE' + GOAL = 'GOAL' + OTHER = 'OTHER' + + name = models.CharField(max_length=128) + unit = models.CharField(max_length=64, blank=True, null=True) + order = models.IntegerField(default=0) + description = models.CharField(max_length=512, blank=True, null=True) + category = models.CharField(max_length=64, choices=((NUTRITION, _('Nutrition')), (ALLERGEN, _('Allergen')), + (PRICE, _('Price')), (GOAL, _('Goal')), (OTHER, _('Other'))), null=True, blank=True) + open_data_slug = models.CharField(max_length=128, null=True, blank=True, default=None) + + # TODO show if empty property? + # TODO formatting property? + + space = models.ForeignKey(Space, on_delete=models.CASCADE) + objects = ScopedManager(space='space') + + def __str__(self): + return f'{self.name}' + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['space', 'name'], name='property_type_unique_name_per_space'), + models.UniqueConstraint(fields=['space', 'open_data_slug'], name='property_type_unique_open_data_slug_per_space') + ] + ordering = ('order',) + + +class Property(models.Model, PermissionModelMixin): + property_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32) + property_type = models.ForeignKey(PropertyType, on_delete=models.PROTECT) + + import_food_id = models.IntegerField(null=True, blank=True) # field to hold food id when importing properties from the open data project + + space = models.ForeignKey(Space, on_delete=models.CASCADE) + objects = ScopedManager(space='space') + + def __str__(self): + return f'{self.property_amount} {self.property_type.unit} {self.property_type.name}' + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['space', 'property_type', 'import_food_id'], name='property_unique_import_food_per_space') + ] + + +class FoodProperty(models.Model): + food = models.ForeignKey(Food, on_delete=models.CASCADE) + property = models.ForeignKey(Property, on_delete=models.CASCADE) + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['food', 'property'], name='property_unique_food') + ] + + class NutritionInformation(models.Model, PermissionModelMixin): fats = models.DecimalField(default=0, decimal_places=16, max_digits=32) carbohydrates = models.DecimalField( @@ -736,14 +828,6 @@ class NutritionInformation(models.Model, PermissionModelMixin): return f'Nutrition {self.pk}' -# class NutritionType(models.Model, PermissionModelMixin): -# name = models.CharField(max_length=128) -# icon = models.CharField(max_length=16, blank=True, null=True) -# description = models.CharField(max_length=512, blank=True, null=True) -# -# space = models.ForeignKey(Space, on_delete=models.CASCADE) -# objects = ScopedManager(space='space') - class RecipeManager(models.Manager.from_queryset(models.QuerySet)): def get_queryset(self): return super(RecipeManager, self).get_queryset().annotate(rating=Avg('cooklog__rating')).annotate(last_cooked=Max('cooklog__created_at')) @@ -766,6 +850,7 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel waiting_time = models.IntegerField(default=0) internal = models.BooleanField(default=False) nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE) + properties = models.ManyToManyField(Property, blank=True) show_ingredient_overview = models.BooleanField(default=True) private = models.BooleanField(default=False) shared = models.ManyToManyField(User, blank=True, related_name='recipe_shared_with') @@ -845,7 +930,6 @@ class RecipeImport(models.Model, PermissionModelMixin): class RecipeBook(ExportModelOperationsMixin('book'), models.Model, PermissionModelMixin): name = models.CharField(max_length=128) description = models.TextField(blank=True) - icon = models.CharField(max_length=16, blank=True, null=True) shared = models.ManyToManyField(User, blank=True, related_name='shared_with') created_by = models.ForeignKey(User, on_delete=models.CASCADE) filter = models.ForeignKey('cookbook.CustomFilter', null=True, blank=True, on_delete=models.SET_NULL) @@ -888,7 +972,6 @@ class RecipeBookEntry(ExportModelOperationsMixin('book_entry'), models.Model, Pe class MealType(models.Model, PermissionModelMixin): name = models.CharField(max_length=128) order = models.IntegerField(default=0) - icon = models.CharField(max_length=16, blank=True, null=True) color = models.CharField(max_length=7, blank=True, null=True) default = models.BooleanField(default=False, blank=True) created_by = models.ForeignKey(User, on_delete=models.CASCADE) @@ -899,6 +982,11 @@ class MealType(models.Model, PermissionModelMixin): def __str__(self): return self.name + class Meta: + constraints = [ + models.UniqueConstraint(fields=['space', 'name'], name='mt_unique_name_per_space'), + ] + class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, PermissionModelMixin): recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, blank=True, null=True) @@ -908,7 +996,8 @@ class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, Permission shared = models.ManyToManyField(User, blank=True, related_name='plan_share') meal_type = models.ForeignKey(MealType, on_delete=models.CASCADE) note = models.TextField(blank=True) - date = models.DateField() + from_date = models.DateField() + to_date = models.DateField() space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') @@ -922,7 +1011,7 @@ class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, Permission return self.meal_type.name def __str__(self): - return f'{self.get_label()} - {self.date} - {self.meal_type.name}' + return f'{self.get_label()} - {self.from_date} - {self.meal_type.name}' class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), models.Model, PermissionModelMixin): @@ -1042,6 +1131,8 @@ class InviteLink(ExportModelOperationsMixin('invite_link'), models.Model, Permis created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) + internal_note = models.TextField(blank=True, null=True) + space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') @@ -1208,7 +1299,7 @@ class UserFile(ExportModelOperationsMixin('user_files'), models.Model, Permissio def is_image(self): try: - img = Image.open(self.file.file.file) + Image.open(self.file.file.file) return True except Exception: return False @@ -1226,10 +1317,25 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis KEYWORD_ALIAS = 'KEYWORD_ALIAS' DESCRIPTION_REPLACE = 'DESCRIPTION_REPLACE' INSTRUCTION_REPLACE = 'INSTRUCTION_REPLACE' + NEVER_UNIT = 'NEVER_UNIT' + TRANSPOSE_WORDS = 'TRANSPOSE_WORDS' + FOOD_REPLACE = 'FOOD_REPLACE' + UNIT_REPLACE = 'UNIT_REPLACE' + NAME_REPLACE = 'NAME_REPLACE' type = models.CharField(max_length=128, - choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')), - (DESCRIPTION_REPLACE, _('Description Replace')), (INSTRUCTION_REPLACE, _('Instruction Replace')),)) + choices=( + (FOOD_ALIAS, _('Food Alias')), + (UNIT_ALIAS, _('Unit Alias')), + (KEYWORD_ALIAS, _('Keyword Alias')), + (DESCRIPTION_REPLACE, _('Description Replace')), + (INSTRUCTION_REPLACE, _('Instruction Replace')), + (NEVER_UNIT, _('Never Unit')), + (TRANSPOSE_WORDS, _('Transpose Words')), + (FOOD_REPLACE, _('Food Replace')), + (UNIT_REPLACE, _('Unit Replace')), + (NAME_REPLACE, _('Name Replace')), + )) name = models.CharField(max_length=128, default='') description = models.TextField(blank=True, null=True) diff --git a/cookbook/schemas.py b/cookbook/schemas.py index 36ce66555..e465de45b 100644 --- a/cookbook/schemas.py +++ b/cookbook/schemas.py @@ -67,17 +67,3 @@ class FilterSchema(AutoSchema): 'schema': {'type': 'string', }, }) return parameters - - -# class QueryOnlySchema(AutoSchema): -# def get_path_parameters(self, path, method): -# if not is_list_view(path, method, self.view): -# return super(QueryOnlySchema, self).get_path_parameters(path, method) - -# parameters = super().get_path_parameters(path, method) -# parameters.append({ -# "name": 'query', "in": "query", "required": False, -# "description": 'Query string matched (fuzzy) against object name.', -# 'schema': {'type': 'string', }, -# }) -# return parameters diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 44bc14223..70ba4b1ae 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -6,30 +6,34 @@ from gettext import gettext as _ from html import escape from smtplib import SMTPException -from django.contrib.auth.models import Group, User, AnonymousUser +from django.contrib.auth.models import AnonymousUser, Group, User +from django.core.cache import caches from django.core.mail import send_mail -from django.db.models import Avg, Q, QuerySet, Sum +from django.db.models import Q, QuerySet, Sum from django.http import BadHeaderError from django.urls import reverse from django.utils import timezone from django_scopes import scopes_disabled from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer -from PIL import Image from oauth2_provider.models import AccessToken +from PIL import Image from rest_framework import serializers from rest_framework.exceptions import NotFound, ValidationError from cookbook.helper.CustomStorageClass import CachedS3Boto3Storage from cookbook.helper.HelperFunctions import str2bool from cookbook.helper.permission_helper import above_space_limit +from cookbook.helper.property_helper import FoodPropertyHelper from cookbook.helper.shopping_helper import RecipeShoppingEditor +from cookbook.helper.unit_conversion_helper import UnitConversionHelper from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, CustomFilter, ExportLog, Food, FoodInheritField, ImportLog, Ingredient, InviteLink, - Keyword, MealPlan, MealType, NutritionInformation, Recipe, RecipeBook, - RecipeBookEntry, RecipeImport, ShareLink, ShoppingList, - ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage, - Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, - SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog) + Keyword, MealPlan, MealType, NutritionInformation, Property, + PropertyType, Recipe, RecipeBook, RecipeBookEntry, RecipeImport, + ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, + Step, Storage, Supermarket, SupermarketCategory, + SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion, + UserFile, UserPreference, UserSpace, ViewLog) from cookbook.templatetags.custom_tags import markdown from recipes.settings import AWS_ENABLED, MEDIA_URL @@ -52,10 +56,9 @@ class ExtendedRecipeMixin(serializers.ModelSerializer): api_serializer = None # extended values are computationally expensive and not needed in normal circumstances try: - if str2bool( - self.context['request'].query_params.get('extended', False)) and self.__class__ == api_serializer: + if str2bool(self.context['request'].query_params.get('extended', False)) and self.__class__ == api_serializer: return fields - except (AttributeError, KeyError) as e: + except (AttributeError, KeyError): pass try: del fields['image'] @@ -74,6 +77,19 @@ class ExtendedRecipeMixin(serializers.ModelSerializer): return path +class OpenDataModelMixin(serializers.ModelSerializer): + + def create(self, validated_data): + if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '': + validated_data['open_data_slug'] = None + return super().create(validated_data) + + def update(self, instance, validated_data): + if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '': + validated_data['open_data_slug'] = None + return super().update(instance, validated_data) + + class CustomDecimalField(serializers.Field): """ Custom decimal field to normalize useless decimal places @@ -86,13 +102,13 @@ class CustomDecimalField(serializers.Field): return round(value, 2).normalize() def to_internal_value(self, data): - if type(data) == int or type(data) == float: + if isinstance(data, int) or isinstance(data, float): return data - elif type(data) == str: + elif isinstance(data, str): if data == '': return 0 try: - return float(data.replace(',', '')) + return float(data.replace(',', '.')) except ValueError: raise ValidationError('A valid number is required') @@ -102,15 +118,21 @@ class CustomOnHandField(serializers.Field): return instance def to_representation(self, obj): - shared_users = None - if request := self.context.get('request', None): - shared_users = getattr(request, '_shared_users', None) - if shared_users is None: + if not self.context["request"].user.is_authenticated: + return [] + shared_users = [] + if c := caches['default'].get(f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None): + shared_users = c + else: try: shared_users = [x.id for x in list(self.context['request'].user.get_shopping_share())] + [ self.context['request'].user.id] + caches['default'].set( + f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', + shared_users, timeout=5 * 60) + # TODO ugly hack that improves API performance significantly, should be done properly except AttributeError: # Anonymous users (using share links) don't have shared users - shared_users = [] + pass return obj.onhand_users.filter(id__in=shared_users).exists() def to_internal_value(self, data): @@ -122,11 +144,11 @@ class SpaceFilterSerializer(serializers.ListSerializer): def to_representation(self, data): if self.context.get('request', None) is None: return - if (type(data) == QuerySet and data.query.is_sliced): + if (isinstance(data, QuerySet) and data.query.is_sliced): # if query is sliced it came from api request not nested serializer return super().to_representation(data) if self.child.Meta.model == User: - if type(self.context['request'].user) == AnonymousUser: + if isinstance(self.context['request'].user, AnonymousUser): data = [] else: data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all() @@ -186,7 +208,7 @@ class UserFileSerializer(serializers.ModelSerializer): def get_preview_link(self, obj): try: - img = Image.open(obj.file.file.file) + Image.open(obj.file.file.file) return self.context['request'].build_absolute_uri(obj.file.url) except Exception: traceback.print_exc() @@ -234,7 +256,7 @@ class UserFileViewSerializer(serializers.ModelSerializer): def get_preview_link(self, obj): try: - img = Image.open(obj.file.file.file) + Image.open(obj.file.file.file) return self.context['request'].build_absolute_uri(obj.file.url) except Exception: traceback.print_exc() @@ -276,10 +298,13 @@ class SpaceSerializer(WritableNestedModelSerializer): class Meta: model = Space - fields = ('id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', - 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb', - 'image', 'use_plural',) - read_only_fields = ('id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',) + fields = ( + 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', + 'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb', + 'image', 'use_plural',) + read_only_fields = ( + 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', + 'demo',) class UserSpaceSerializer(WritableNestedModelSerializer): @@ -296,8 +321,8 @@ class UserSpaceSerializer(WritableNestedModelSerializer): class Meta: model = UserSpace - fields = ('id', 'user', 'space', 'groups', 'active', 'created_at', 'updated_at',) - read_only_fields = ('id', 'created_at', 'updated_at', 'space') + fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',) + read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space') class SpacedModelSerializer(serializers.ModelSerializer): @@ -309,13 +334,16 @@ class SpacedModelSerializer(serializers.ModelSerializer): class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer): def create(self, validated_data): + validated_data['name'] = validated_data['name'].strip() + space = validated_data.pop('space', self.context['request'].space) validated_data['created_by'] = self.context['request'].user - return super().create(validated_data) + obj, created = MealType.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data) + return obj class Meta: list_serializer_class = SpaceFilterSerializer model = MealType - fields = ('id', 'name', 'order', 'icon', 'color', 'default', 'created_by') + fields = ('id', 'name', 'order', 'color', 'default', 'created_by') read_only_fields = ('created_by',) @@ -349,7 +377,7 @@ class UserPreferenceSerializer(WritableNestedModelSerializer): 'food_inherit_default', 'default_delay', 'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix', - 'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'food_children_exist' + 'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients', 'food_children_exist' ) @@ -422,25 +450,26 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin): class Meta: model = Keyword fields = ( - 'id', 'name', 'icon', 'label', 'description', 'image', 'parent', 'numchild', 'numrecipe', 'created_at', + 'id', 'name', 'label', 'description', 'image', 'parent', 'numchild', 'numrecipe', 'created_at', 'updated_at', 'full_name') read_only_fields = ('id', 'label', 'numchild', 'parent', 'image') -class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin): +class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin, OpenDataModelMixin): recipe_filter = 'steps__ingredients__unit' def create(self, validated_data): - name = validated_data.pop('name').strip() + # get_or_create drops any field that contains '__' when creating so values must be included in validated data + space = validated_data.pop('space', self.context['request'].space) + if x := validated_data.get('name', None): + validated_data['name'] = x.strip() + if x := validated_data.get('name', None): + validated_data['plural_name'] = x.strip() - if plural_name := validated_data.pop('plural_name', None): - plural_name = plural_name.strip() - - if unit := Unit.objects.filter(Q(name=name) | Q(plural_name=name)).first(): + if unit := Unit.objects.filter(Q(name__iexact=validated_data['name']) | Q(plural_name__iexact=validated_data['name']), space=space).first(): return unit - space = validated_data.pop('space', self.context['request'].space) - obj, created = Unit.objects.get_or_create(name=name, plural_name=plural_name, space=space, defaults=validated_data) + obj, created = Unit.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data) return obj def update(self, instance, validated_data): @@ -451,16 +480,16 @@ class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin): class Meta: model = Unit - fields = ('id', 'name', 'plural_name', 'description', 'numrecipe', 'image') + fields = ('id', 'name', 'plural_name', 'description', 'base_unit', 'numrecipe', 'image', 'open_data_slug') read_only_fields = ('id', 'numrecipe', 'image') -class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer): +class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer, OpenDataModelMixin): def create(self, validated_data): - name = validated_data.pop('name').strip() + validated_data['name'] = validated_data['name'].strip() space = validated_data.pop('space', self.context['request'].space) - obj, created = SupermarketCategory.objects.get_or_create(name=name, space=space) + obj, created = SupermarketCategory.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data) return obj def update(self, instance, validated_data): @@ -479,12 +508,45 @@ class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer): fields = ('id', 'category', 'supermarket', 'order') -class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer): +class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataModelMixin): category_to_supermarket = SupermarketCategoryRelationSerializer(many=True, read_only=True) + def create(self, validated_data): + validated_data['name'] = validated_data['name'].strip() + space = validated_data.pop('space', self.context['request'].space) + obj, created = Supermarket.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data) + return obj + class Meta: model = Supermarket - fields = ('id', 'name', 'description', 'category_to_supermarket') + fields = ('id', 'name', 'description', 'category_to_supermarket', 'open_data_slug') + + +class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer, UniqueFieldsMixin): + id = serializers.IntegerField(required=False) + + def create(self, validated_data): + validated_data['name'] = validated_data['name'].strip() + space = validated_data.pop('space', self.context['request'].space) + obj, created = PropertyType.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data) + return obj + + class Meta: + model = PropertyType + fields = ('id', 'name', 'unit', 'description', 'order', 'open_data_slug') + + +class PropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer): + property_type = PropertyTypeSerializer() + property_amount = CustomDecimalField() + + def create(self, validated_data): + validated_data['space'] = self.context['request'].space + return super().create(validated_data) + + class Meta: + model = Property + fields = ('id', 'property_amount', 'property_type') class RecipeSimpleSerializer(WritableNestedModelSerializer): @@ -512,10 +574,9 @@ class FoodSimpleSerializer(serializers.ModelSerializer): fields = ('id', 'name', 'plural_name') -class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedRecipeMixin): +class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedRecipeMixin, OpenDataModelMixin): supermarket_category = SupermarketCategorySerializer(allow_null=True, required=False) recipe = RecipeSimpleSerializer(allow_null=True, required=False) - # shopping = serializers.SerializerMethodField('get_shopping_status') shopping = serializers.ReadOnlyField(source='shopping_status') inherit_fields = FoodInheritFieldSerializer(many=True, allow_null=True, required=False) child_inherit_fields = FoodInheritFieldSerializer(many=True, allow_null=True, required=False) @@ -523,19 +584,30 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR substitute_onhand = serializers.SerializerMethodField('get_substitute_onhand') substitute = FoodSimpleSerializer(many=True, allow_null=True, required=False) + properties = PropertySerializer(many=True, allow_null=True, required=False) + properties_food_unit = UnitSerializer(allow_null=True, required=False) + properties_food_amount = CustomDecimalField(required=False) + recipe_filter = 'steps__ingredients__food' images = ['recipe__image'] def get_substitute_onhand(self, obj): - shared_users = None - if request := self.context.get('request', None): - shared_users = getattr(request, '_shared_users', None) - if shared_users is None: + if not self.context["request"].user.is_authenticated: + return [] + shared_users = [] + if c := caches['default'].get( + f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None): + shared_users = c + else: try: shared_users = [x.id for x in list(self.context['request'].user.get_shopping_share())] + [ self.context['request'].user.id] - except AttributeError: - shared_users = [] + caches['default'].set( + f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', + shared_users, timeout=5 * 60) + # TODO ugly hack that improves API performance significantly, should be done properly + except AttributeError: # Anonymous users (using share links) don't have shared users + pass filter = Q(id__in=obj.substitute.all()) if obj.substitute_siblings: filter |= Q(path__startswith=obj.path[:Food.steplen * (obj.depth - 1)], depth=obj.depth) @@ -543,11 +615,8 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR filter |= Q(path__startswith=obj.path, depth__gt=obj.depth) return Food.objects.filter(filter).filter(onhand_users__id__in=shared_users).exists() - # def get_shopping_status(self, obj): - # return ShoppingListEntry.objects.filter(space=obj.space, food=obj, checked=False).count() > 0 - def create(self, validated_data): - name = validated_data.pop('name').strip() + name = validated_data['name'].strip() if plural_name := validated_data.pop('plural_name', None): plural_name = plural_name.strip() @@ -568,7 +637,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR validated_data['recipe'] = Recipe.objects.get(**recipe) # assuming if on hand for user also onhand for shopping_share users - if not onhand is None: + if onhand is not None: shared_users = [user := self.context['request'].user] + list(user.userpreference.shopping_share.all()) if self.instance: onhand_users = self.instance.onhand_users.all() @@ -579,7 +648,18 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR else: validated_data['onhand_users'] = list(set(onhand_users) - set(shared_users)) - obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, defaults=validated_data) + if properties_food_unit := validated_data.pop('properties_food_unit', None): + properties_food_unit = Unit.objects.filter(name=properties_food_unit['name']).first() + + properties = validated_data.pop('properties', None) + + obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, properties_food_unit=properties_food_unit, + defaults=validated_data) + + if properties and len(properties) > 0: + for p in properties: + obj.properties.add(Property.objects.create(property_type_id=p['property_type']['id'], property_amount=p['property_amount'], space=space)) + return obj def update(self, instance, validated_data): @@ -590,7 +670,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR # assuming if on hand for user also onhand for shopping_share users onhand = validated_data.get('food_onhand', None) reset_inherit = self.initial_data.get('reset_inherit', False) - if not onhand is None: + if onhand is not None: shared_users = [user := self.context['request'].user] + list(user.userpreference.shopping_share.all()) if onhand: validated_data['onhand_users'] = list(self.instance.onhand_users.all()) + shared_users @@ -606,9 +686,11 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR class Meta: model = Food fields = ( - 'id', 'name', 'plural_name', 'description', 'shopping', 'recipe', 'food_onhand', 'supermarket_category', + 'id', 'name', 'plural_name', 'description', 'shopping', 'recipe', 'url', + 'properties', 'properties_food_amount', 'properties_food_unit', + 'food_onhand', 'supermarket_category', 'image', 'parent', 'numchild', 'numrecipe', 'inherit_fields', 'full_name', 'ignore_shopping', - 'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields' + 'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields', 'open_data_slug', ) read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe') @@ -618,9 +700,24 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer): unit = UnitSerializer(allow_null=True) used_in_recipes = serializers.SerializerMethodField('get_used_in_recipes') amount = CustomDecimalField() + conversions = serializers.SerializerMethodField('get_conversions') def get_used_in_recipes(self, obj): - return list(Recipe.objects.filter(steps__ingredients=obj.id).values('id', 'name')) + used_in = [] + for s in obj.step_set.all(): + for r in s.recipe_set.all(): + used_in.append({'id': r.id, 'name': r.name}) + return used_in + + def get_conversions(self, obj): + if obj.unit and obj.food: + uch = UnitConversionHelper(self.context['request'].space) + conversions = [] + for c in uch.get_conversions(obj): + conversions.append({'food': c.food.name, 'unit': c.unit.name, 'amount': c.amount}) # TODO do formatting in helper + return conversions + else: + return [] def create(self, validated_data): validated_data['space'] = self.context['request'].space @@ -633,10 +730,11 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer): class Meta: model = Ingredient fields = ( - 'id', 'food', 'unit', 'amount', 'note', 'order', + 'id', 'food', 'unit', 'amount', 'conversions', 'note', 'order', 'is_header', 'no_amount', 'original_text', 'used_in_recipes', 'always_use_plural_unit', 'always_use_plural_food', ) + read_only_fields = ['conversions', ] class IngredientSerializer(IngredientSimpleSerializer): @@ -667,14 +765,15 @@ class StepSerializer(WritableNestedModelSerializer, ExtendedRecipeMixin): def get_step_recipe_data(self, obj): # check if root type is recipe to prevent infinite recursion # can be improved later to allow multi level embedding - if obj.step_recipe and type(self.parent.root) == RecipeSerializer: + if obj.step_recipe and isinstance(self.parent.root, RecipeSerializer): return StepRecipeSerializer(obj.step_recipe, context={'request': self.context['request']}).data class Meta: model = Step fields = ( 'id', 'name', 'instruction', 'ingredients', 'ingredients_markdown', - 'ingredients_vue', 'time', 'order', 'show_as_header', 'file', 'step_recipe', 'step_recipe_data', 'numrecipe' + 'ingredients_vue', 'time', 'order', 'show_as_header', 'file', 'step_recipe', + 'step_recipe_data', 'numrecipe', 'show_ingredients_table' ) @@ -688,6 +787,38 @@ class StepRecipeSerializer(WritableNestedModelSerializer): ) +class UnitConversionSerializer(WritableNestedModelSerializer, OpenDataModelMixin): + name = serializers.SerializerMethodField('get_conversion_name') + base_unit = UnitSerializer() + converted_unit = UnitSerializer() + food = FoodSerializer(allow_null=True, required=False) + base_amount = CustomDecimalField() + converted_amount = CustomDecimalField() + + def get_conversion_name(self, obj): + text = f'{round(obj.base_amount)} {obj.base_unit} ' + if obj.food: + text += f' {obj.food}' + return text + f' = {round(obj.converted_amount)} {obj.converted_unit}' + + def create(self, validated_data): + validated_data['space'] = validated_data.pop('space', self.context['request'].space) + try: + return UnitConversion.objects.get( + food__name__iexact=validated_data.get('food', {}).get('name', None), + base_unit__name__iexact=validated_data.get('base_unit', {}).get('name', None), + converted_unit__name__iexact=validated_data.get('converted_unit', {}).get('name', None), + space=validated_data['space'] + ) + except UnitConversion.DoesNotExist: + validated_data['created_by'] = self.context['request'].user + return super().create(validated_data) + + class Meta: + model = UnitConversion + fields = ('id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug') + + class NutritionInformationSerializer(serializers.ModelSerializer): carbohydrates = CustomDecimalField() fats = CustomDecimalField() @@ -738,21 +869,28 @@ class RecipeOverviewSerializer(RecipeBaseSerializer): class RecipeSerializer(RecipeBaseSerializer): nutrition = NutritionInformationSerializer(allow_null=True, required=False) + properties = PropertySerializer(many=True, required=False) steps = StepSerializer(many=True) keywords = KeywordSerializer(many=True) shared = UserSerializer(many=True, required=False) rating = CustomDecimalField(required=False, allow_null=True, read_only=True) last_cooked = serializers.DateTimeField(required=False, allow_null=True, read_only=True) + food_properties = serializers.SerializerMethodField('get_food_properties') + + def get_food_properties(self, obj): + fph = FoodPropertyHelper(obj.space) # initialize with object space since recipes might be viewed anonymously + return fph.calculate_recipe_properties(obj) class Meta: model = Recipe fields = ( 'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time', 'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url', - 'internal', 'show_ingredient_overview', 'nutrition', 'servings', 'file_path', 'servings_text', 'rating', 'last_cooked', + 'internal', 'show_ingredient_overview', 'nutrition', 'properties', 'food_properties', 'servings', 'file_path', 'servings_text', 'rating', + 'last_cooked', 'private', 'shared', ) - read_only_fields = ['image', 'created_by', 'created_at'] + read_only_fields = ['image', 'created_by', 'created_at', 'food_properties'] def validate(self, data): above_limit, msg = above_space_limit(self.context['request'].space) @@ -810,7 +948,7 @@ class RecipeBookSerializer(SpacedModelSerializer, WritableNestedModelSerializer) class Meta: model = RecipeBook - fields = ('id', 'name', 'description', 'icon', 'shared', 'created_by', 'filter') + fields = ('id', 'name', 'description', 'shared', 'created_by', 'filter') read_only_fields = ('created_by',) @@ -827,8 +965,7 @@ class RecipeBookEntrySerializer(serializers.ModelSerializer): def create(self, validated_data): book = validated_data['book'] recipe = validated_data['recipe'] - if not book.get_owner() == self.context['request'].user and not self.context[ - 'request'].user in book.get_shared(): + if not book.get_owner() == self.context['request'].user and not self.context['request'].user in book.get_shared(): raise NotFound(detail=None, code=None) obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe) return obj @@ -866,12 +1003,22 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer): model = MealPlan fields = ( 'id', 'title', 'recipe', 'servings', 'note', 'note_markdown', - 'date', 'meal_type', 'created_by', 'shared', 'recipe_name', + 'from_date', 'to_date', 'meal_type', 'created_by', 'shared', 'recipe_name', 'meal_type_name', 'shopping' ) read_only_fields = ('created_by',) +class AutoMealPlanSerializer(serializers.Serializer): + start_date = serializers.DateField() + end_date = serializers.DateField() + meal_type_id = serializers.IntegerField() + keywords = KeywordSerializer(many=True) + servings = CustomDecimalField() + shared = UserSerializer(many=True, required=False, allow_null=True) + addshopping = serializers.BooleanField() + + class ShoppingListRecipeSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField('get_name') # should this be done at the front end? recipe_name = serializers.ReadOnlyField(source='recipe.name') @@ -884,10 +1031,10 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): value = value.quantize( Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero return ( - obj.name - or getattr(obj.mealplan, 'title', None) - or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) - or obj.recipe.name + obj.name + or getattr(obj.mealplan, 'title', None) + or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) + or obj.recipe.name ) + f' ({value:.2g})' def update(self, instance, validated_data): @@ -1089,13 +1236,19 @@ class InviteLinkSerializer(WritableNestedModelSerializer): if obj.email: try: - if InviteLink.objects.filter(space=self.context['request'].space, created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20: - message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(self.context['request'].user.get_user_display_name()) - message += _(' to join their Tandoor Recipes space ') + escape(self.context['request'].space.name) + '.\n\n' - message += _('Click the following link to activate your account: ') + self.context['request'].build_absolute_uri(reverse('view_invite', args=[str(obj.uuid)])) + '\n\n' - message += _('If the link does not work use the following code to manually join the space: ') + str(obj.uuid) + '\n\n' + if InviteLink.objects.filter(space=self.context['request'].space, + created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20: + message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape( + self.context['request'].user.get_user_display_name()) + message += _(' to join their Tandoor Recipes space ') + escape( + self.context['request'].space.name) + '.\n\n' + message += _('Click the following link to activate your account: ') + self.context[ + 'request'].build_absolute_uri(reverse('view_invite', args=[str(obj.uuid)])) + '\n\n' + message += _('If the link does not work use the following code to manually join the space: ') + str( + obj.uuid) + '\n\n' message += _('The invitation is valid until ') + str(obj.valid_until) + '\n\n' - message += _('Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub ') + 'https://github.com/vabene1111/recipes/' + message += _( + 'Tandoor Recipes is an Open Source recipe manager. Check it out on GitHub ') + 'https://github.com/vabene1111/recipes/' send_mail( _('Tandoor Recipes Invite'), @@ -1112,7 +1265,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer): class Meta: model = InviteLink fields = ( - 'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'created_by', 'created_at',) + 'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by', 'created_at',) read_only_fields = ('id', 'uuid', 'created_by', 'created_at',) @@ -1164,7 +1317,7 @@ class AccessTokenSerializer(serializers.ModelSerializer): class KeywordExportSerializer(KeywordSerializer): class Meta: model = Keyword - fields = ('name', 'icon', 'description', 'created_at', 'updated_at') + fields = ('name', 'description', 'created_at', 'updated_at') class NutritionInformationExportSerializer(NutritionInformationSerializer): @@ -1204,7 +1357,8 @@ class IngredientExportSerializer(WritableNestedModelSerializer): class Meta: model = Ingredient - fields = ('food', 'unit', 'amount', 'note', 'order', 'is_header', 'no_amount', 'always_use_plural_unit', 'always_use_plural_food') + fields = ('food', 'unit', 'amount', 'note', 'order', 'is_header', 'no_amount', 'always_use_plural_unit', + 'always_use_plural_food') class StepExportSerializer(WritableNestedModelSerializer): @@ -1216,7 +1370,7 @@ class StepExportSerializer(WritableNestedModelSerializer): class Meta: model = Step - fields = ('name', 'instruction', 'ingredients', 'time', 'order', 'show_as_header') + fields = ('name', 'instruction', 'ingredients', 'time', 'order', 'show_as_header', 'show_ingredients_table') class RecipeExportSerializer(WritableNestedModelSerializer): @@ -1228,7 +1382,7 @@ class RecipeExportSerializer(WritableNestedModelSerializer): model = Recipe fields = ( 'name', 'description', 'keywords', 'steps', 'working_time', - 'waiting_time', 'internal', 'nutrition', 'servings', 'servings_text', + 'waiting_time', 'internal', 'nutrition', 'servings', 'servings_text', 'source_url', ) def create(self, validated_data): diff --git a/cookbook/signals.py b/cookbook/signals.py index 624ffa536..4636ca8af 100644 --- a/cookbook/signals.py +++ b/cookbook/signals.py @@ -1,18 +1,19 @@ -from decimal import Decimal from functools import wraps from django.conf import settings from django.contrib.auth.models import User from django.contrib.postgres.search import SearchVector +from django.core.cache import caches from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import translation from django_scopes import scope, scopes_disabled +from cookbook.helper.cache_helper import CacheHelper from cookbook.helper.shopping_helper import RecipeShoppingEditor from cookbook.managers import DICTIONARY -from cookbook.models import (Food, FoodInheritField, Ingredient, MealPlan, Recipe, - ShoppingListEntry, Step, UserPreference, SearchPreference, SearchFields) +from cookbook.models import (Food, MealPlan, PropertyType, Recipe, SearchFields, SearchPreference, + Step, Unit, UserPreference) SQLITE = True if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', @@ -149,3 +150,15 @@ def auto_add_shopping(sender, instance=None, created=False, weak=False, **kwargs print("MEAL_AUTO_ADD Created SLR") except AttributeError: pass + + +@receiver(post_save, sender=Unit) +def clear_unit_cache(sender, instance=None, created=False, **kwargs): + if instance: + caches['default'].delete(CacheHelper(instance.space).BASE_UNITS_CACHE_KEY) + + +@receiver(post_save, sender=PropertyType) +def clear_property_type_cache(sender, instance=None, created=False, **kwargs): + if instance: + caches['default'].delete(CacheHelper(instance.space).PROPERTY_TYPE_CACHE_KEY) diff --git a/cookbook/static/assets/spinner.svg b/cookbook/static/assets/spinner.svg index 4c8ab12de..72cc67892 100644 --- a/cookbook/static/assets/spinner.svg +++ b/cookbook/static/assets/spinner.svg @@ -7,8 +7,7 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="2000px" - height="2000px" + viewBox="0 0 2000 2000" version="1.1" id="SVGRoot" diff --git a/cookbook/static/themes/tandoor_dark.min.css b/cookbook/static/themes/tandoor_dark.min.css new file mode 100644 index 000000000..67cc1ac1b --- /dev/null +++ b/cookbook/static/themes/tandoor_dark.min.css @@ -0,0 +1,10486 @@ +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(/static/webfonts/poppins_devanagari_400.woff2) format('woff2'); + unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB; +} + +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(/static/webfonts/poppins_latin_ext_400.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(/static/webfonts/poppins_latin_400.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(/static/webfonts/poppins_devanagari_500.woff2) format('woff2'); + unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB; +} + +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(/static/webfonts/poppins_latin_ext_500.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(/static/webfonts/poppins_latin_500.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(/static/webfonts/poppins_devanagari_700.woff2) format('woff2'); + unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB; +} + +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(/static/webfonts/poppins_latin_ext_700.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(/static/webfonts/poppins_latin_700.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + + +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + -- #a7240e: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #b98766; + --secondary: #b55e4f; + --success: #82aa8b; + --info: #385f84; + --warning: #eaaa21; + --danger: #a7240e; + --light: #cfd5cd; + --dark: #221e1e; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace +} + +*, :after, :before { + box-sizing: border-box +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block +} + +body { + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #fff; + text-align: left; + background-color: rgb(36 36 36); +} + +[tabindex="-1"]:focus { + outline: 0 !important +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: .5rem +} + +p { + margin-top: 0; + margin-bottom: 1rem +} + +abbr[data-original-title], abbr[title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none +} + +address { + font-style: normal; + line-height: inherit +} + +address, dl, ol, ul { + margin-bottom: 1rem +} + +dl, ol, ul { + margin-top: 0 +} + +ol ol, ol ul, ul ol, ul ul { + margin-bottom: 0 +} + +dt { + font-weight: 700 +} + +dd { + margin-bottom: .5rem; + margin-left: 0 +} + +blockquote { + margin: 0 0 1rem +} + +b, strong { + font-weight: bolder +} + +small { + font-size: 80% +} + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +a { + color: #b98766; + text-decoration: none; + background-color: transparent +} + +a:hover { + color: #000; + text-decoration: underline +} + +a:not([href]):not([tabindex]), a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover { + color: inherit; + text-decoration: none +} + +a:not([href]):not([tabindex]):focus { + outline: 0 +} + +code, kbd, pre, samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace; + font-size: 1em +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto +} + +figure { + margin: 0 0 1rem +} + +img { + border-style: none +} + +img, svg { + vertical-align: middle +} + +svg { + overflow: hidden +} + +table { + border-collapse: collapse; + color: #fff!important; +} + +caption { + padding-top: .75rem; + padding-bottom: .75rem; + color: #6e6e6e; + text-align: left; + caption-side: bottom +} + +th { + text-align: inherit +} + +label { + display: inline-block; + margin-bottom: .5rem +} + +button { + border-radius: 0 +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color +} + +button, input, optgroup, select, textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +button, input { + overflow: visible +} + +button, select { + text-transform: none +} + +select { + word-wrap: normal +} + +[type=button], [type=reset], [type=submit], button { + -webkit-appearance: button +} + +[type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled), button:not(:disabled) { + cursor: pointer +} + +[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner { + padding: 0; + border-style: none +} + +input[type=checkbox], input[type=radio] { + box-sizing: border-box; + padding: 0 +} + +input[type=date], input[type=datetime-local], input[type=month], input[type=time] { + -webkit-appearance: listbox +} + +textarea { + overflow: auto; + resize: vertical +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0 +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal +} + +progress { + vertical-align: baseline +} + +[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + outline-offset: -2px; + -webkit-appearance: none +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button +} + +output { + display: inline-block +} + +summary { + display: list-item; + cursor: pointer +} + +template { + display: none +} + +[hidden] { + display: none !important +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + margin-bottom: .5rem; + font-weight: 500; + line-height: 1.2 +} + +.h1, h1 { + font-size: 2.5rem +} + +.h2, h2 { + font-size: 2rem +} + +.h3, h3 { + font-size: 1.75rem +} + +.h4, h4 { + font-size: 1.5rem +} + +.h5, h5 { + font-size: 1.25rem +} + +.h6, h6 { + font-size: 1rem +} + +.lead { + font-size: 1.25rem; + font-weight: 300 +} + +.display-1 { + font-size: 6rem +} + +.display-1, .display-2 { + font-weight: 300; + line-height: 1.2 +} + +.display-2 { + font-size: 5.5rem +} + +.display-3 { + font-size: 4.5rem +} + +.display-3, .display-4 { + font-weight: 300; + line-height: 1.2 +} + +.display-4 { + font-size: 3.5rem +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, .1) +} + +.small, small { + font-size: 80%; + font-weight: 400 +} + +.mark, mark { + padding: .2em; + background-color: #fcf8e3 +} + +.list-inline, .list-unstyled { + padding-left: 0; + list-style: none +} + +.list-inline-item { + display: inline-block +} + +.list-inline-item:not(:last-child) { + margin-right: .5rem +} + +.initialism { + font-size: 90%; + text-transform: uppercase +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d +} + +.blockquote-footer:before { + content: "\2014\00A0" +} + +.img-fluid, .img-thumbnail { + max-width: 100%; + height: auto +} + +.img-thumbnail { + padding: .25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: .25rem +} + +.figure { + display: inline-block +} + +.figure-img { + margin-bottom: .5rem; + line-height: 1 +} + +.figure-caption { + font-size: 90%; + color: #6c757d +} + +code { + font-size: 87.5%; + color: #e83e8c; + word-break: break-word +} + +a > code { + color: inherit +} + +kbd { + padding: .2rem .4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: .2rem +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700 +} + +pre { + display: block; + font-size: 87.5%; + color: #212529 +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +@media (min-width: 576px) { + .container { + max-width: 540px + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px + } +} + +.container-fluid { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px +} + +.no-gutters { + margin-right: 0; + margin-left: 0 +} + +.no-gutters > .col, .no-gutters > [class*=col-] { + padding-right: 0; + padding-left: 0 +} + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col-auto, .col-lg, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-auto, .col-md, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md-auto, .col-sm, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px +} + +.col { + flex-basis: 0; + flex-grow: 1; + max-width: 100% +} + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: 100% +} + +.col-1 { + flex: 0 0 8.33333%; + max-width: 8.33333% +} + +.col-2 { + flex: 0 0 16.66667%; + max-width: 16.66667% +} + +.col-3 { + flex: 0 0 25%; + max-width: 25% +} + +.col-4 { + flex: 0 0 33.33333%; + max-width: 33.33333% +} + +.col-5 { + flex: 0 0 41.66667%; + max-width: 41.66667% +} + +.col-6 { + flex: 0 0 50%; + max-width: 50% +} + +.col-7 { + flex: 0 0 58.33333%; + max-width: 58.33333% +} + +.col-8 { + flex: 0 0 66.66667%; + max-width: 66.66667% +} + +.col-9 { + flex: 0 0 75%; + max-width: 75% +} + +.col-10 { + flex: 0 0 83.33333%; + max-width: 83.33333% +} + +.col-11 { + flex: 0 0 91.66667%; + max-width: 91.66667% +} + +.col-12 { + flex: 0 0 100%; + max-width: 100% +} + +.order-first { + order: -1 +} + +.order-last { + order: 13 +} + +.order-0 { + order: 0 +} + +.order-1 { + order: 1 +} + +.order-2 { + order: 2 +} + +.order-3 { + order: 3 +} + +.order-4 { + order: 4 +} + +.order-5 { + order: 5 +} + +.order-6 { + order: 6 +} + +.order-7 { + order: 7 +} + +.order-8 { + order: 8 +} + +.order-9 { + order: 9 +} + +.order-10 { + order: 10 +} + +.order-11 { + order: 11 +} + +.order-12 { + order: 12 +} + +.offset-1 { + margin-left: 8.33333% +} + +.offset-2 { + margin-left: 16.66667% +} + +.offset-3 { + margin-left: 25% +} + +.offset-4 { + margin-left: 33.33333% +} + +.offset-5 { + margin-left: 41.66667% +} + +.offset-6 { + margin-left: 50% +} + +.offset-7 { + margin-left: 58.33333% +} + +.offset-8 { + margin-left: 66.66667% +} + +.offset-9 { + margin-left: 75% +} + +.offset-10 { + margin-left: 83.33333% +} + +.offset-11 { + margin-left: 91.66667% +} + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + max-width: 100% + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-sm-1 { + flex: 0 0 8.33333%; + max-width: 8.33333% + } + + .col-sm-2 { + flex: 0 0 16.66667%; + max-width: 16.66667% + } + + .col-sm-3 { + flex: 0 0 25%; + max-width: 25% + } + + .col-sm-4 { + flex: 0 0 33.33333%; + max-width: 33.33333% + } + + .col-sm-5 { + flex: 0 0 41.66667%; + max-width: 41.66667% + } + + .col-sm-6 { + flex: 0 0 50%; + max-width: 50% + } + + .col-sm-7 { + flex: 0 0 58.33333%; + max-width: 58.33333% + } + + .col-sm-8 { + flex: 0 0 66.66667%; + max-width: 66.66667% + } + + .col-sm-9 { + flex: 0 0 75%; + max-width: 75% + } + + .col-sm-10 { + flex: 0 0 83.33333%; + max-width: 83.33333% + } + + .col-sm-11 { + flex: 0 0 91.66667%; + max-width: 91.66667% + } + + .col-sm-12 { + flex: 0 0 100%; + max-width: 100% + } + + .order-sm-first { + order: -1 + } + + .order-sm-last { + order: 13 + } + + .order-sm-0 { + order: 0 + } + + .order-sm-1 { + order: 1 + } + + .order-sm-2 { + order: 2 + } + + .order-sm-3 { + order: 3 + } + + .order-sm-4 { + order: 4 + } + + .order-sm-5 { + order: 5 + } + + .order-sm-6 { + order: 6 + } + + .order-sm-7 { + order: 7 + } + + .order-sm-8 { + order: 8 + } + + .order-sm-9 { + order: 9 + } + + .order-sm-10 { + order: 10 + } + + .order-sm-11 { + order: 11 + } + + .order-sm-12 { + order: 12 + } + + .offset-sm-0 { + margin-left: 0 + } + + .offset-sm-1 { + margin-left: 8.33333% + } + + .offset-sm-2 { + margin-left: 16.66667% + } + + .offset-sm-3 { + margin-left: 25% + } + + .offset-sm-4 { + margin-left: 33.33333% + } + + .offset-sm-5 { + margin-left: 41.66667% + } + + .offset-sm-6 { + margin-left: 50% + } + + .offset-sm-7 { + margin-left: 58.33333% + } + + .offset-sm-8 { + margin-left: 66.66667% + } + + .offset-sm-9 { + margin-left: 75% + } + + .offset-sm-10 { + margin-left: 83.33333% + } + + .offset-sm-11 { + margin-left: 91.66667% + } +} + +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + max-width: 100% + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-md-1 { + flex: 0 0 8.33333%; + max-width: 8.33333% + } + + .col-md-2 { + flex: 0 0 16.66667%; + max-width: 16.66667% + } + + .col-md-3 { + flex: 0 0 25%; + max-width: 25% + } + + .col-md-4 { + flex: 0 0 33.33333%; + max-width: 33.33333% + } + + .col-md-5 { + flex: 0 0 41.66667%; + max-width: 41.66667% + } + + .col-md-6 { + flex: 0 0 50%; + max-width: 50% + } + + .col-md-7 { + flex: 0 0 58.33333%; + max-width: 58.33333% + } + + .col-md-8 { + flex: 0 0 66.66667%; + max-width: 66.66667% + } + + .col-md-9 { + flex: 0 0 75%; + max-width: 75% + } + + .col-md-10 { + flex: 0 0 83.33333%; + max-width: 83.33333% + } + + .col-md-11 { + flex: 0 0 91.66667%; + max-width: 91.66667% + } + + .col-md-12 { + flex: 0 0 100%; + max-width: 100% + } + + .order-md-first { + order: -1 + } + + .order-md-last { + order: 13 + } + + .order-md-0 { + order: 0 + } + + .order-md-1 { + order: 1 + } + + .order-md-2 { + order: 2 + } + + .order-md-3 { + order: 3 + } + + .order-md-4 { + order: 4 + } + + .order-md-5 { + order: 5 + } + + .order-md-6 { + order: 6 + } + + .order-md-7 { + order: 7 + } + + .order-md-8 { + order: 8 + } + + .order-md-9 { + order: 9 + } + + .order-md-10 { + order: 10 + } + + .order-md-11 { + order: 11 + } + + .order-md-12 { + order: 12 + } + + .offset-md-0 { + margin-left: 0 + } + + .offset-md-1 { + margin-left: 8.33333% + } + + .offset-md-2 { + margin-left: 16.66667% + } + + .offset-md-3 { + margin-left: 25% + } + + .offset-md-4 { + margin-left: 33.33333% + } + + .offset-md-5 { + margin-left: 41.66667% + } + + .offset-md-6 { + margin-left: 50% + } + + .offset-md-7 { + margin-left: 58.33333% + } + + .offset-md-8 { + margin-left: 66.66667% + } + + .offset-md-9 { + margin-left: 75% + } + + .offset-md-10 { + margin-left: 83.33333% + } + + .offset-md-11 { + margin-left: 91.66667% + } +} + +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + max-width: 100% + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-lg-1 { + flex: 0 0 8.33333%; + max-width: 8.33333% + } + + .col-lg-2 { + flex: 0 0 16.66667%; + max-width: 16.66667% + } + + .col-lg-3 { + flex: 0 0 25%; + max-width: 25% + } + + .col-lg-4 { + flex: 0 0 33.33333%; + max-width: 33.33333% + } + + .col-lg-5 { + flex: 0 0 41.66667%; + max-width: 41.66667% + } + + .col-lg-6 { + flex: 0 0 50%; + max-width: 50% + } + + .col-lg-7 { + flex: 0 0 58.33333%; + max-width: 58.33333% + } + + .col-lg-8 { + flex: 0 0 66.66667%; + max-width: 66.66667% + } + + .col-lg-9 { + flex: 0 0 75%; + max-width: 75% + } + + .col-lg-10 { + flex: 0 0 83.33333%; + max-width: 83.33333% + } + + .col-lg-11 { + flex: 0 0 91.66667%; + max-width: 91.66667% + } + + .col-lg-12 { + flex: 0 0 100%; + max-width: 100% + } + + .order-lg-first { + order: -1 + } + + .order-lg-last { + order: 13 + } + + .order-lg-0 { + order: 0 + } + + .order-lg-1 { + order: 1 + } + + .order-lg-2 { + order: 2 + } + + .order-lg-3 { + order: 3 + } + + .order-lg-4 { + order: 4 + } + + .order-lg-5 { + order: 5 + } + + .order-lg-6 { + order: 6 + } + + .order-lg-7 { + order: 7 + } + + .order-lg-8 { + order: 8 + } + + .order-lg-9 { + order: 9 + } + + .order-lg-10 { + order: 10 + } + + .order-lg-11 { + order: 11 + } + + .order-lg-12 { + order: 12 + } + + .offset-lg-0 { + margin-left: 0 + } + + .offset-lg-1 { + margin-left: 8.33333% + } + + .offset-lg-2 { + margin-left: 16.66667% + } + + .offset-lg-3 { + margin-left: 25% + } + + .offset-lg-4 { + margin-left: 33.33333% + } + + .offset-lg-5 { + margin-left: 41.66667% + } + + .offset-lg-6 { + margin-left: 50% + } + + .offset-lg-7 { + margin-left: 58.33333% + } + + .offset-lg-8 { + margin-left: 66.66667% + } + + .offset-lg-9 { + margin-left: 75% + } + + .offset-lg-10 { + margin-left: 83.33333% + } + + .offset-lg-11 { + margin-left: 91.66667% + } +} + +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + max-width: 100% + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-xl-1 { + flex: 0 0 8.33333%; + max-width: 8.33333% + } + + .col-xl-2 { + flex: 0 0 16.66667%; + max-width: 16.66667% + } + + .col-xl-3 { + flex: 0 0 25%; + max-width: 25% + } + + .col-xl-4 { + flex: 0 0 33.33333%; + max-width: 33.33333% + } + + .col-xl-5 { + flex: 0 0 41.66667%; + max-width: 41.66667% + } + + .col-xl-6 { + flex: 0 0 50%; + max-width: 50% + } + + .col-xl-7 { + flex: 0 0 58.33333%; + max-width: 58.33333% + } + + .col-xl-8 { + flex: 0 0 66.66667%; + max-width: 66.66667% + } + + .col-xl-9 { + flex: 0 0 75%; + max-width: 75% + } + + .col-xl-10 { + flex: 0 0 83.33333%; + max-width: 83.33333% + } + + .col-xl-11 { + flex: 0 0 91.66667%; + max-width: 91.66667% + } + + .col-xl-12 { + flex: 0 0 100%; + max-width: 100% + } + + .order-xl-first { + order: -1 + } + + .order-xl-last { + order: 13 + } + + .order-xl-0 { + order: 0 + } + + .order-xl-1 { + order: 1 + } + + .order-xl-2 { + order: 2 + } + + .order-xl-3 { + order: 3 + } + + .order-xl-4 { + order: 4 + } + + .order-xl-5 { + order: 5 + } + + .order-xl-6 { + order: 6 + } + + .order-xl-7 { + order: 7 + } + + .order-xl-8 { + order: 8 + } + + .order-xl-9 { + order: 9 + } + + .order-xl-10 { + order: 10 + } + + .order-xl-11 { + order: 11 + } + + .order-xl-12 { + order: 12 + } + + .offset-xl-0 { + margin-left: 0 + } + + .offset-xl-1 { + margin-left: 8.33333% + } + + .offset-xl-2 { + margin-left: 16.66667% + } + + .offset-xl-3 { + margin-left: 25% + } + + .offset-xl-4 { + margin-left: 33.33333% + } + + .offset-xl-5 { + margin-left: 41.66667% + } + + .offset-xl-6 { + margin-left: 50% + } + + .offset-xl-7 { + margin-left: 58.33333% + } + + .offset-xl-8 { + margin-left: 66.66667% + } + + .offset-xl-9 { + margin-left: 75% + } + + .offset-xl-10 { + margin-left: 83.33333% + } + + .offset-xl-11 { + margin-left: 91.66667% + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + color: #212529 +} + +.table td, .table th { + padding: .75rem; + vertical-align: top; + border-top: 1px solid #dee2e6 +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6 +} + +.table tbody + tbody { + border-top: 2px solid #dee2e6 +} + +.table-sm td, .table-sm th { + padding: .3rem +} + +.table-borde#a7240e, .table-borde#a7240e td, .table-borde#a7240e th { + border: 1px solid #dee2e6 +} + +.table-borde#a7240e thead td, .table-borde#a7240e thead th { + border-bottom-width: 2px +} + +.table-borderless tbody + tbody, .table-borderless td, .table-borderless th, .table-borderless thead th { + border: 0 +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, .05) +} + +.table-hover tbody tr:hover { + color: #212529; + background-color: rgba(0, 0, 0, .075) +} + +.table-primary, .table-primary > td, .table-primary > th { + background-color: #b8b9c7 +} + +.table-primary tbody + tbody, .table-primary td, .table-primary th, .table-primary thead th { + border-color: #7a7e97 +} + +.table-hover .table-primary:hover, .table-hover .table-primary:hover > td, .table-hover .table-primary:hover > th { + background-color: #aaabbc +} + +.table-secondary, .table-secondary > td, .table-secondary > th { + background-color: #ececfb +} + +.table-secondary tbody + tbody, .table-secondary td, .table-secondary th, .table-secondary thead th { + border-color: #dcdcf8 +} + +.table-hover .table-secondary:hover, .table-hover .table-secondary:hover > td, .table-hover .table-secondary:hover > th { + background-color: #d7d7f7 +} + +.table-success, .table-success > td, .table-success > th { + background-color: #d2f4ea +} + +.table-success tbody + tbody, .table-success td, .table-success th, .table-success thead th { + border-color: #abead7 +} + +.table-hover .table-success:hover, .table-hover .table-success:hover > td, .table-hover .table-success:hover > th { + background-color: #beefe0 +} + +.table-info, .table-info > td, .table-info > th { + background-color: #bfe2ff +} + +.table-info tbody + tbody, .table-info td, .table-info th, .table-info thead th { + border-color: #87c9ff +} + +.table-hover .table-info:hover, .table-hover .table-info:hover > td, .table-hover .table-info:hover > th { + background-color: #a6d6ff +} + +.table-warning, .table-warning > td, .table-warning > th { + background-color: #ffe8b8 +} + +.table-warning tbody + tbody, .table-warning td, .table-warning th, .table-warning thead th { + border-color: #ffd47a +} + +.table-hover .table-warning:hover, .table-hover .table-warning:hover > td, .table-hover .table-warning:hover > th { + background-color: #ffe09f +} + +.table-danger, .table-danger > td, .table-danger > th { + background-color: #ffb8b8 +} + +.table-danger tbody + tbody, .table-danger td, .table-danger th, .table-danger thead th { + border-color: #ff7a7a +} + +.table-hover .table-danger:hover, .table-hover .table-danger:hover > td, .table-hover .table-danger:hover > th { + background-color: #ff9f9f +} + +.table-light, .table-light > td, .table-light > th { + background-color: #fed8ca +} + +.table-light tbody + tbody, .table-light td, .table-light th, .table-light thead th { + border-color: #fdb69c +} + +.table-hover .table-light:hover, .table-hover .table-light:hover > td, .table-hover .table-light:hover > th { + background-color: #fec6b1 +} + +.table-dark, .table-dark > td, .table-dark > th { + background-color: #c1c0c0 +} + +.table-dark tbody + tbody, .table-dark td, .table-dark th, .table-dark thead th { + border-color: #8c8a8a +} + +.table-hover .table-dark:hover, .table-hover .table-dark:hover > td, .table-hover .table-dark:hover > th { + background-color: #b4b3b3 +} + +.table-active, .table-active > td, .table-active > th, .table-hover .table-active:hover, .table-hover .table-active:hover > td, .table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, .075) +} + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55 +} + +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6 +} + +.table-dark { + color: #fff; + background-color: #343a40 +} + +.table-dark td, .table-dark th, .table-dark thead th { + border-color: #454d55 +} + +.table-dark.table-borde#a7240e { + border: 0 +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: hsla(0, 0%, 100%, .05) +} + +.table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: hsla(0, 0%, 100%, .075) +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-sm > .table-borde#a7240e { + border: 0 + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-md > .table-borde#a7240e { + border: 0 + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-lg > .table-borde#a7240e { + border: 0 + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-xl > .table-borde#a7240e { + border: 0 + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch +} + +.table-responsive > .table-borde#a7240e { + border: 0 +} + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + padding: .375rem .75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: .25rem; + transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .form-control { + transition: none + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0 +} + +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #0014b7; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.form-control::-webkit-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::-moz-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::-ms-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1 +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff +} + +.form-control-file, .form-control-range { + display: block; + width: 100% +} + +.col-form-label { + padding-top: calc(.375rem + 1px); + padding-bottom: calc(.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5 +} + +.col-form-label-lg { + padding-top: calc(.5rem + 1px); + padding-bottom: calc(.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5 +} + +.col-form-label-sm { + padding-top: calc(.25rem + 1px); + padding-bottom: calc(.25rem + 1px); + font-size: .875rem; + line-height: 1.5 +} + +.form-control-plaintext { + display: block; + width: 100%; + padding-top: .375rem; + padding-bottom: .375rem; + margin-bottom: 0; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0 +} + +.form-control-plaintext.form-control-lg, .form-control-plaintext.form-control-sm { + padding-right: 0; + padding-left: 0 +} + +.form-control-sm { + height: calc(1.5em + .5rem + 2px); + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +select.form-control[multiple], select.form-control[size], textarea.form-control { + height: auto +} + +.form-group { + margin-bottom: 1rem +} + +.form-text { + display: block; + margin-top: .25rem +} + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px +} + +.form-row > .col, .form-row > [class*=col-] { + padding-right: 5px; + padding-left: 5px +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem +} + +.form-check-input { + position: absolute; + margin-top: .3rem; + margin-left: -1.25rem +} + +.form-check-input:disabled ~ .form-check-label { + color: #6e6e6e +} + +.form-check-label { + margin-bottom: 0 +} + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: .75rem +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: .3125rem; + margin-left: 0 +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: .25rem; + font-size: 80%; + color: #82aa8b +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .25rem .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1.5; + color: #212529; + background-color: rgba(94, 214, 179, .9); + border-radius: .25rem +} + +.form-control.is-valid, .was-validated .form-control:valid { + border-color: #82aa8b; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2382aa8b' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: 100% calc(.375em + .1875rem); + background-size: calc(.75em + .375rem) calc(.75em + .375rem) +} + +.form-control.is-valid:focus, .was-validated .form-control:valid:focus { + border-color: #82aa8b; + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .25) +} + +.form-control.is-valid ~ .valid-feedback, .form-control.is-valid ~ .valid-tooltip, .was-validated .form-control:valid ~ .valid-feedback, .was-validated .form-control:valid ~ .valid-tooltip { + display: block +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem) +} + +.custom-select.is-valid, .was-validated .custom-select:valid { + border-color: #82aa8b; + padding-right: calc((3em + 2.25rem) / 4 + 1.75rem); + background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px, url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2382aa8b' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) +} + +.custom-select.is-valid:focus, .was-validated .custom-select:valid:focus { + border-color: #82aa8b; + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .25) +} + +.custom-select.is-valid ~ .valid-feedback, .custom-select.is-valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, .form-control-file.is-valid ~ .valid-tooltip, .was-validated .custom-select:valid ~ .valid-feedback, .was-validated .custom-select:valid ~ .valid-tooltip, .was-validated .form-control-file:valid ~ .valid-feedback, .was-validated .form-control-file:valid ~ .valid-tooltip { + display: block +} + +.form-check-input.is-valid ~ .form-check-label, .was-validated .form-check-input:valid ~ .form-check-label { + color: #82aa8b +} + +.form-check-input.is-valid ~ .valid-feedback, .form-check-input.is-valid ~ .valid-tooltip, .was-validated .form-check-input:valid ~ .valid-feedback, .was-validated .form-check-input:valid ~ .valid-tooltip { + display: block +} + +.custom-control-input.is-valid ~ .custom-control-label, .was-validated .custom-control-input:valid ~ .custom-control-label { + color: #82aa8b +} + +.custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .custom-control-input:valid ~ .custom-control-label:before { + border-color: #82aa8b +} + +.custom-control-input.is-valid ~ .valid-feedback, .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .custom-control-input:valid ~ .valid-feedback, .was-validated .custom-control-input:valid ~ .valid-tooltip { + display: block +} + +.custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .custom-control-input:valid:checked ~ .custom-control-label:before { + border-color: #87e0c6; + background-color: #87e0c6 +} + +.custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .custom-control-input:valid:focus ~ .custom-control-label:before { + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .25) +} + +.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label:before, .custom-file-input.is-valid ~ .custom-file-label, .was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label:before, .was-validated .custom-file-input:valid ~ .custom-file-label { + border-color: #82aa8b +} + +.custom-file-input.is-valid ~ .valid-feedback, .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .custom-file-input:valid ~ .valid-feedback, .was-validated .custom-file-input:valid ~ .valid-tooltip { + display: block +} + +.custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .custom-file-input:valid:focus ~ .custom-file-label { + border-color: #82aa8b; + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .25) +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: .25rem; + font-size: 80%; + color: #a7240e +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .25rem .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(255, 0, 0, .9); + border-radius: .25rem +} + +.form-control.is-invalid, .was-validated .form-control:invalid { + border-color: #a7240e; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#a7240e' viewBox='-2 -2 7 7'%3E%3Cpath stroke='#a7240e' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: 100% calc(.375em + .1875rem); + background-size: calc(.75em + .375rem) calc(.75em + .375rem) +} + +.form-control.is-invalid:focus, .was-validated .form-control:invalid:focus { + border-color: #a7240e; + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .25) +} + +.form-control.is-invalid ~ .invalid-feedback, .form-control.is-invalid ~ .invalid-tooltip, .was-validated .form-control:invalid ~ .invalid-feedback, .was-validated .form-control:invalid ~ .invalid-tooltip { + display: block +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem) +} + +.custom-select.is-invalid, .was-validated .custom-select:invalid { + border-color: #a7240e; + padding-right: calc((3em + 2.25rem) / 4 + 1.75rem); + background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px, url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#a7240e' viewBox='-2 -2 7 7'%3E%3Cpath stroke='#a7240e' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) +} + +.custom-select.is-invalid:focus, .was-validated .custom-select:invalid:focus { + border-color: #a7240e; + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .25) +} + +.custom-select.is-invalid ~ .invalid-feedback, .custom-select.is-invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, .form-control-file.is-invalid ~ .invalid-tooltip, .was-validated .custom-select:invalid ~ .invalid-feedback, .was-validated .custom-select:invalid ~ .invalid-tooltip, .was-validated .form-control-file:invalid ~ .invalid-feedback, .was-validated .form-control-file:invalid ~ .invalid-tooltip { + display: block +} + +.form-check-input.is-invalid ~ .form-check-label, .was-validated .form-check-input:invalid ~ .form-check-label { + color: #a7240e +} + +.form-check-input.is-invalid ~ .invalid-feedback, .form-check-input.is-invalid ~ .invalid-tooltip, .was-validated .form-check-input:invalid ~ .invalid-feedback, .was-validated .form-check-input:invalid ~ .invalid-tooltip { + display: block +} + +.custom-control-input.is-invalid ~ .custom-control-label, .was-validated .custom-control-input:invalid ~ .custom-control-label { + color: #a7240e +} + +.custom-control-input.is-invalid ~ .custom-control-label:before, .was-validated .custom-control-input:invalid ~ .custom-control-label:before { + border-color: #a7240e +} + +.custom-control-input.is-invalid ~ .invalid-feedback, .custom-control-input.is-invalid ~ .invalid-tooltip, .was-validated .custom-control-input:invalid ~ .invalid-feedback, .was-validated .custom-control-input:invalid ~ .invalid-tooltip { + display: block +} + +.custom-control-input.is-invalid:checked ~ .custom-control-label:before, .was-validated .custom-control-input:invalid:checked ~ .custom-control-label:before { + border-color: #f33; + background-color: #f33 +} + +.custom-control-input.is-invalid:focus ~ .custom-control-label:before, .was-validated .custom-control-input:invalid:focus ~ .custom-control-label:before { + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .25) +} + +.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label:before, .custom-file-input.is-invalid ~ .custom-file-label, .was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label:before, .was-validated .custom-file-input:invalid ~ .custom-file-label { + border-color: #a7240e +} + +.custom-file-input.is-invalid ~ .invalid-feedback, .custom-file-input.is-invalid ~ .invalid-tooltip, .was-validated .custom-file-input:invalid ~ .invalid-feedback, .was-validated .custom-file-input:invalid ~ .invalid-tooltip { + display: block +} + +.custom-file-input.is-invalid:focus ~ .custom-file-label, .was-validated .custom-file-input:invalid:focus ~ .custom-file-label { + border-color: #a7240e; + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .25) +} + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center +} + +.form-inline .form-check { + width: 100% +} + +@media (min-width: 576px) { + .form-inline label { + justify-content: center + } + + .form-inline .form-group, .form-inline label { + display: flex; + align-items: center; + margin-bottom: 0 + } + + .form-inline .form-group { + flex: 0 0 auto; + flex-flow: row wrap + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .form-inline .form-control-plaintext { + display: inline-block + } + + .form-inline .custom-select, .form-inline .input-group { + width: auto + } + + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0 + } + + .form-inline .form-check-input { + position: relative; + flex-shrink: 0; + margin-top: 0; + margin-right: .25rem; + margin-left: 0 + } + + .form-inline .custom-control { + align-items: center; + justify-content: center + } + + .form-inline .custom-control-label { + margin-bottom: 0 + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #fff; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + line-height: 1.5; + border-radius: .1875rem; + background-color: transparent; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .btn { + transition: none + } +} + +.btn:hover { + color: #212529; + text-decoration: none +} + +.btn.focus, .btn:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.btn.disabled, .btn:disabled { + opacity: .65 +} + +a.btn.disabled, fieldset:disabled a.btn { + pointer-events: none +} + +.btn-primary { + background-color: #b98766; + border-color: #b98766 +} + +.btn-primary:hover { + color: #fff; + background-color: #000211; + border-color: #000004 +} + +.btn-primary.focus, .btn-primary:focus { + box-shadow: 0 0 0 .2rem rgba(38, 43, 85, .5) +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #b98766; + border-color: #b98766 +} + +.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #000004; + border-color: #000 +} + +.btn-primary:not(:disabled):not(.disabled).active:focus, .btn-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(38, 43, 85, .5) +} + +.btn-secondary { + color: #212529; + background-color: #b55e4f; + border-color: #b55e4f +} + +.btn-secondary:hover { + color: #212529; + background-color: #9b9bec; + border-color: #9090ea +} + +.btn-secondary.focus, .btn-secondary:focus { + box-shadow: 0 0 0 .2rem rgba(164, 165, 212, .5) +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #212529; + background-color: #b55e4f; + border-color: #b55e4f +} + +.btn-secondary:not(:disabled):not(.disabled).active, .btn-secondary:not(:disabled):not(.disabled):active, .show > .btn-secondary.dropdown-toggle { + color: #212529; + background-color: #9090ea; + border-color: #8585e8 +} + +.btn-secondary:not(:disabled):not(.disabled).active:focus, .btn-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(164, 165, 212, .5) +} + +.btn-success { + color: #212529; + background-color: #82aa8b; + border-color: #82aa8b +} + +.btn-success:hover { + color: #212529; + background-color: #40cea5; + border-color: #35cca0 +} + +.btn-success.focus, .btn-success:focus { + box-shadow: 0 0 0 .2rem rgba(85, 187, 158, .5) +} + +.btn-success.disabled, .btn-success:disabled { + color: #212529; + background-color: #82aa8b; + border-color: #82aa8b +} + +.btn-success:not(:disabled):not(.disabled).active, .btn-success:not(:disabled):not(.disabled):active, .show > .btn-success.dropdown-toggle { + color: #212529; + background-color: #35cca0; + border-color: #32c398 +} + +.btn-success:not(:disabled):not(.disabled).active:focus, .btn-success:not(:disabled):not(.disabled):active:focus, .show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(85, 187, 158, .5) +} + +.btn-info { + background-color: #385f84; + border-color: #385f84 +} + +.btn-info:hover { + color: #fff; + background-color: #0085f2; + border-color: #007ee5 +} + +.btn-info.focus, .btn-info:focus { + box-shadow: 0 0 0 .2rem rgba(60, 167, 255, .5) +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #385f84; + border-color: #385f84 +} + +.btn-info:not(:disabled):not(.disabled).active, .btn-info:not(:disabled):not(.disabled):active, .show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #007ee5; + border-color: #0077d8 +} + +.btn-info:not(:disabled):not(.disabled).active:focus, .btn-info:not(:disabled):not(.disabled):active:focus, .show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(60, 167, 255, .5) +} + +.btn-warning { + color: #212529; + background-color: #eaaa21; + border-color: #eaaa21 +} + +.btn-warning:hover { + color: #212529; + background-color: #d99300; + border-color: #cc8a00 +} + +.btn-warning.focus, .btn-warning:focus { + box-shadow: 0 0 0 .2rem rgba(222, 153, 6, .5) +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #eaaa21; + border-color: #eaaa21 +} + +.btn-warning:not(:disabled):not(.disabled).active, .btn-warning:not(:disabled):not(.disabled):active, .show > .btn-warning.dropdown-toggle { + color: #fff; + background-color: #cc8a00; + border-color: #bf8200 +} + +.btn-warning:not(:disabled):not(.disabled).active:focus, .btn-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(222, 153, 6, .5) +} + +.btn-danger { + background-color: #a7240e; + border-color: #a7240e +} + +.btn-danger:hover { + color: #fff; + background-color: #d90000; + border-color: #c00 +} + +.btn-danger.focus, .btn-danger:focus { + box-shadow: 0 0 0 .2rem rgba(255, 38, 38, .5) +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #a7240e; + border-color: #a7240e +} + +.btn-danger:not(:disabled):not(.disabled).active, .btn-danger:not(:disabled):not(.disabled):active, .show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #c00; + border-color: #bf0000 +} + +.btn-danger:not(:disabled):not(.disabled).active:focus, .btn-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(255, 38, 38, .5) +} + +.btn-light { + color: #212529; + background-color: #cfd5cd; + border-color: #cfd5cd +} + +.btn-light:hover { + color: #fff; + background-color: #fb571b; + border-color: #fb4e0f +} + +.btn-light.focus, .btn-light:focus { + box-shadow: 0 0 0 .2rem rgba(219, 103, 61, .5) +} + +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #cfd5cd; + border-color: #cfd5cd +} + +.btn-light:not(:disabled):not(.disabled).active, .btn-light:not(:disabled):not(.disabled):active, .show > .btn-light.dropdown-toggle { + color: #fff; + background-color: #fb4e0f; + border-color: #f94604 +} + +.btn-light:not(:disabled):not(.disabled).active:focus, .btn-light:not(:disabled):not(.disabled):active:focus, .show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(219, 103, 61, .5) +} + +.btn-dark { + background-color: #221e1e; + border-color: #221e1e +} + +.btn-dark:hover { + color: #fff; + background-color: #0e0c0c; + border-color: #070606 +} + +.btn-dark.focus, .btn-dark:focus { + box-shadow: 0 0 0 .2rem rgba(67, 64, 64, .5) +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #221e1e; + border-color: #221e1e +} + +.btn-dark:not(:disabled):not(.disabled).active, .btn-dark:not(:disabled):not(.disabled):active, .show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #070606; + border-color: #000 +} + +.btn-dark:not(:disabled):not(.disabled).active:focus, .btn-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(67, 64, 64, .5) +} + +.btn-outline-primary, .btn-outline-primary:hover { + border-color: #b98766 +} + +.btn-outline-primary.focus, .btn-outline-primary:focus { + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .5) +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #b98766; + background-color: transparent +} + +.btn-outline-primary:not(:disabled):not(.disabled).active, .btn-outline-primary:not(:disabled):not(.disabled):active, .show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: transparent; + border-color: #b98766 +} + +.btn-outline-primary:not(:disabled):not(.disabled).active:focus, .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .5) +} + +.btn-outline-secondary { + border-color: #b55e4f +} + +.btn-outline-secondary:hover { + color: #212529; + border-color: #b55e4f +} + +.btn-outline-secondary.focus, .btn-outline-secondary:focus { + box-shadow: 0 0 0 .2rem rgba(187, 187, 242, .5) +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #b55e4f; + background-color: transparent +} + +.btn-outline-secondary:not(:disabled):not(.disabled).active, .btn-outline-secondary:not(:disabled):not(.disabled):active, .show > .btn-outline-secondary.dropdown-toggle { + color: #212529; + background-color: #b55e4f; + border-color: #b55e4f +} + +.btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(187, 187, 242, .5) +} + +.btn-outline-success { + border-color: #82aa8b +} + +.btn-outline-success:hover { + color: #212529; + border-color: #82aa8b +} + +.btn-outline-success.focus, .btn-outline-success:focus { + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .5) +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #82aa8b; + background-color: transparent +} + +.btn-outline-success:not(:disabled):not(.disabled).active, .btn-outline-success:not(:disabled):not(.disabled):active, .show > .btn-outline-success.dropdown-toggle { + color: #212529; + background-color: #82aa8b; + border-color: #82aa8b +} + +.btn-outline-success:not(:disabled):not(.disabled).active:focus, .btn-outline-success:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .5) +} + +.btn-outline-info, .btn-outline-info:hover { + border-color: #385f84 +} + +.btn-outline-info.focus, .btn-outline-info:focus { + box-shadow: 0 0 0 .2rem rgba(56, 95, 132, .5) +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #385f84; + background-color: transparent +} + +.btn-outline-info:not(:disabled):not(.disabled).active, .btn-outline-info:not(:disabled):not(.disabled):active, .show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #385f84; + border-color: #385f84 +} + +.btn-outline-info:not(:disabled):not(.disabled).active:focus, .btn-outline-info:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(25, 152, 255, .5) +} + +.btn-outline-warning { + border-color: #eaaa21 +} + +.btn-outline-warning:hover { + color: #212529; + border-color: #eaaa21 +} + +.btn-outline-warning.focus, .btn-outline-warning:focus { + box-shadow: 0 0 0 .2rem rgba(255, 173, 0, .5) +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #eaaa21; + background-color: transparent +} + +.btn-outline-warning:not(:disabled):not(.disabled).active, .btn-outline-warning:not(:disabled):not(.disabled):active, .show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #eaaa21; + border-color: #eaaa21 +} + +.btn-outline-warning:not(:disabled):not(.disabled).active:focus, .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(255, 173, 0, .5) +} + +.btn-outline-danger, .btn-outline-danger:hover { + border-color: #a7240e +} + +.btn-outline-danger.focus, .btn-outline-danger:focus { + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .5) +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #a7240e; + background-color: transparent +} + +.btn-outline-danger:not(:disabled):not(.disabled).active, .btn-outline-danger:not(:disabled):not(.disabled):active, .show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #a7240e; + border-color: #a7240e +} + +.btn-outline-danger:not(:disabled):not(.disabled).active:focus, .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .5) +} + +.btn-outline-light { + border-color: #cfd5cd +} + +.btn-outline-light:hover { + color: #212529; + border-color: #cfd5cd +} + +.btn-outline-light.focus, .btn-outline-light:focus { + box-shadow: 0 0 0 .2rem rgba(252, 115, 65, .5) +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #cfd5cd; + background-color: transparent +} + +.btn-outline-light:not(:disabled):not(.disabled).active, .btn-outline-light:not(:disabled):not(.disabled):active, .show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #cfd5cd; + border-color: #cfd5cd +} + +.btn-outline-light:not(:disabled):not(.disabled).active:focus, .btn-outline-light:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(252, 115, 65, .5) +} + +.btn-outline-dark, .btn-outline-dark:hover { + border-color: #221e1e +} + +.btn-outline-dark.focus, .btn-outline-dark:focus { + box-shadow: 0 0 0 .2rem rgba(34, 30, 30, .5) +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #221e1e; + background-color: transparent +} + +.btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled):active, .show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #221e1e; + border-color: #221e1e +} + +.btn-outline-dark:not(:disabled):not(.disabled).active:focus, .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(34, 30, 30, .5) +} + +.btn-link { + font-weight: 400; + color: #b98766; + text-decoration: none +} + +.btn-link:hover { + color: #000; + text-decoration: underline +} + +.btn-link.focus, .btn-link:focus { + text-decoration: underline; + box-shadow: none +} + +.btn-link.disabled, .btn-link:disabled { + color: #6c757d; + pointer-events: none +} + +.btn-group-lg > .btn, .btn-lg { + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +.btn-group-sm > .btn, .btn-sm { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block + .btn-block { + margin-top: .5rem +} + +input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block { + width: 100% +} + + + +.btn { + font-size: .875rem; + font-family: Poppins, sans-serif; + padding: .625rem 1.25rem; + outline: none; + line-height: 1.5; +} + +.btn.btn-rounded { + border-radius: 50px +} + +.btn.btn-white { + background: #fff; + transition: all .5s ease-in-out +} + +.btn.btn-white:hover { + background: #a7240e; + color: #fff +} + +.btn:focus { + box-shadow: none +} + +.btn-primary { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-primary:hover { + background: transparent; + color: #b98766; + border: 1px solid #b98766 +} + +.btn-secondary { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-secondary:hover { + background: transparent; + color: #b55e4f; + border: 1px solid #b55e4f +} + +.btn-success { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-success:hover { + background: transparent; + color: #82aa8b; + border: 1px solid #82aa8b +} + +.btn-info { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-info:hover { + background: transparent; + color: #385f84; + border: 1px solid #385f84 +} + +.btn-warning { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-warning:hover { + background: transparent; + color: #eaaa21; + border: 1px solid #eaaa21 +} + +.btn-danger { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-danger:hover { + background: transparent; + color: #a7240e; + border: 1px solid #a7240e +} + +.btn-light { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-light:hover { + background-color: hsla(0, 0%, 18%, .5); + color: #cfd5cd; + border: 1px solid hsla(0, 0%, 18%, .5) +} + +.btn-dark { + transition: all .5s ease-in-out; + color: #fff +} + +.btn-dark:hover { + background: transparent; + color: #221e1e; + border: 1px solid #221e1e +} + +.btn-opacity-primary { + color: #b98766; + background-color: #0012a7; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-primary:hover { + color: #b98766; + background-color: #fff; + border: 2px solid #b98766 +} + +.btn-opacity-secondary { + color: #b55e4f; + background-color: #fff; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-secondary:hover { + color: #b55e4f; + background-color: #fff; + border: 2px solid #b55e4f +} + +.btn-opacity-success { + color: #82aa8b; + background-color: #b7eddd; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-success:hover { + color: #82aa8b; + background-color: #fff; + border: 2px solid #82aa8b +} + +.btn-opacity-info { + color: #385f84; + background-color: #89caff; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-info:hover { + color: #385f84; + background-color: #fff; + border: 2px solid #385f84 +} + +.btn-opacity-warning { + color: #eaaa21; + background-color: #ffd170; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-warning:hover { + color: #eaaa21; + background-color: #fff; + border: 2px solid #eaaa21 +} + +.btn-opacity-danger { + color: #a7240e; + background-color: #ff7070; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-danger:hover { + color: #a7240e; + background-color: #fff; + border: 2px solid #a7240e +} + +.btn-opacity-light { + color: #cfd5cd; + background-color: #fec4af; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-light:hover { + color: #cfd5cd; + background-color: #fff; + border: 2px solid #cfd5cd +} + +.btn-opacity-dark { + color: #221e1e; + background-color: #5e5353; + border: 2px solid transparent; + transition: all .5s ease-in-out +} + +.btn-opacity-dark:hover { + color: #221e1e; + background-color: #fff; + border: 2px solid #221e1e +} + +.btn-outline-primary { + color: #b98766; + background-color: transparent; + border: 2px solid #b98766; + transition: all .5s ease-in-out +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #b98766 +} + +.btn-outline-secondary { + color: #b55e4f; + background-color: transparent; + border: 2px solid #b55e4f; + transition: all .5s ease-in-out +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #b55e4f +} + +.btn-outline-success { + color: #82aa8b; + background-color: #fff; + border: 2px solid #82aa8b; + transition: all .5s ease-in-out +} + +.btn-outline-success:hover { + color: #fff; + background-color: #82aa8b +} + +.btn-outline-info { + color: #385f84; + background-color: transparent; + border: 2px solid #385f84; + transition: all .5s ease-in-out +} + +.btn-outline-info:hover { + color: #fff; + background-color: #385f84 +} + +.btn-outline-warning { + color: #eaaa21; + background-color: #fff; + border: 2px solid #eaaa21; + transition: all .5s ease-in-out +} + +.btn-outline-warning:hover { + color: #fff; + background-color: #eaaa21 +} + +.btn-outline-danger { + color: #a7240e; + background-color: transparent; + border: 2px solid #a7240e; + transition: all .5s ease-in-out +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #a7240e +} + +.btn-outline-light { + color: #cfd5cd; + background-color: transparent; + border: 2px solid #cfd5cd; + transition: all .5s ease-in-out +} + +.btn-outline-light:hover { + color: #fff; + background-color: #cfd5cd +} + +.btn-outline-dark { + color: #221e1e; + background-color: transparent; + border: 2px solid #221e1e; + transition: all .5s ease-in-out +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #221e1e +} + + +.fade { + transition: opacity .15s linear +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .fade { + transition: none + } +} + +.fade:not(.show) { + opacity: 0 +} + +.collapse:not(.show) { + display: none +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height .35s ease +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .collapsing { + transition: none + } +} + +.dropdown, .dropleft, .dropright, .dropup { + position: relative +} + +.dropdown-toggle { + white-space: nowrap +} + +.dropdown-toggle:after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid; + border-right: .3em solid transparent; + border-bottom: 0; + border-left: .3em solid transparent +} + +.dropdown-toggle:empty:after { + margin-left: 0 +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: .5rem 0; + margin: .125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: rgb(36 36 36); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: .25rem +} + +.dropdown-menu-left { + right: auto; + left: 0 +} + +.dropdown-menu-right { + right: 0; + left: auto +} + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0 + } + + .dropdown-menu-sm-right { + right: 0; + left: auto + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0 + } + + .dropdown-menu-md-right { + right: 0; + left: auto + } +} + +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0 + } + + .dropdown-menu-lg-right { + right: 0; + left: auto + } +} + +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0 + } + + .dropdown-menu-xl-right { + right: 0; + left: auto + } +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: .125rem +} + +.dropup .dropdown-toggle:after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: 0; + border-right: .3em solid transparent; + border-bottom: .3em solid; + border-left: .3em solid transparent +} + +.dropup .dropdown-toggle:empty:after { + margin-left: 0 +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: .125rem +} + +.dropright .dropdown-toggle:after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid transparent; + border-right: 0; + border-bottom: .3em solid transparent; + border-left: .3em solid +} + +.dropright .dropdown-toggle:empty:after { + margin-left: 0 +} + +.dropright .dropdown-toggle:after { + vertical-align: 0 +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: .125rem +} + +.dropleft .dropdown-toggle:after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + display: none +} + +.dropleft .dropdown-toggle:before { + display: inline-block; + margin-right: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid transparent; + border-right: .3em solid; + border-bottom: .3em solid transparent +} + +.dropleft .dropdown-toggle:empty:after { + margin-left: 0 +} + +.dropleft .dropdown-toggle:before { + vertical-align: 0 +} + +.dropdown-menu[x-placement^=bottom], .dropdown-menu[x-placement^=left], .dropdown-menu[x-placement^=right], .dropdown-menu[x-placement^=top] { + right: auto; + bottom: auto +} + +.dropdown-divider { + height: 0; + margin: .5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef +} + +.dropdown-item { + display: block; + width: 100%; + padding: .25rem 1.5rem; + clear: both; + font-weight: 400; + color: #fff; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0 +} + +.dropdown-item:focus, .dropdown-item:hover { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #b98766 +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent +} + +.dropdown-menu.show { + display: block +} + +.dropdown-header { + display: block; + padding: .5rem 1.5rem; + margin-bottom: 0; + font-size: .875rem; + color: #6c757d; + white-space: nowrap +} + +.dropdown-item-text { + display: block; + padding: .25rem 1.5rem; + color: #212529 +} + +.btn-group, .btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle +} + +.btn-group-vertical > .btn, .btn-group > .btn { + position: relative; + flex: 1 1 auto +} + +.btn-group-vertical > .btn.active, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:hover, .btn-group > .btn.active, .btn-group > .btn:active, .btn-group > .btn:focus, .btn-group > .btn:hover { + z-index: 1 +} + +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start +} + +.btn-toolbar .input-group { + width: auto +} + +.btn-group > .btn-group:not(:first-child), .btn-group > .btn:not(:first-child) { + margin-left: -1px +} + +.btn-group > .btn-group:not(:last-child) > .btn, .btn-group > .btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group > .btn-group:not(:first-child) > .btn, .btn-group > .btn:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.dropdown-toggle-split { + padding-right: .9375rem; + padding-left: .9375rem +} + +.dropdown-toggle-split:after, .dropright .dropdown-toggle-split:after, .dropup .dropdown-toggle-split:after { + margin-left: 0 +} + +.dropleft .dropdown-toggle-split:before { + margin-right: 0 +} + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.8203125rem; + line-height: 1.5; + border-radius: 0.2rem +} + +.btn-group-sm > .btn + .dropdown-toggle-split, .btn-sm + .dropdown-toggle-split { + padding-right: .375rem; + padding-left: .375rem +} + +.btn-group-lg > .btn + .dropdown-toggle-split, .btn-lg + .dropdown-toggle-split { + padding-right: .75rem; + padding-left: .75rem +} + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center +} + +.btn-group-vertical > .btn, .btn-group-vertical > .btn-group { + width: 100% +} + +.btn-group-vertical > .btn-group:not(:first-child), .btn-group-vertical > .btn:not(:first-child) { + margin-top: -1px +} + +.btn-group-vertical > .btn-group:not(:last-child) > .btn, .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn-group:not(:first-child) > .btn, .btn-group-vertical > .btn:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.btn-group-toggle > .btn, .btn-group-toggle > .btn-group > .btn { + margin-bottom: 0 +} + +.btn-group-toggle > .btn-group > .btn input[type=checkbox], .btn-group-toggle > .btn-group > .btn input[type=radio], .btn-group-toggle > .btn input[type=checkbox], .btn-group-toggle > .btn input[type=radio] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none +} + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100% +} + +.input-group > .custom-file, .input-group > .custom-select, .input-group > .form-control, .input-group > .form-control-plaintext { + position: relative; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0 +} + +.input-group > .custom-file + .custom-file, .input-group > .custom-file + .custom-select, .input-group > .custom-file + .form-control, .input-group > .custom-select + .custom-file, .input-group > .custom-select + .custom-select, .input-group > .custom-select + .form-control, .input-group > .form-control + .custom-file, .input-group > .form-control + .custom-select, .input-group > .form-control + .form-control, .input-group > .form-control-plaintext + .custom-file, .input-group > .form-control-plaintext + .custom-select, .input-group > .form-control-plaintext + .form-control { + margin-left: -1px +} + +.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label, .input-group > .custom-select:focus, .input-group > .form-control:focus { + z-index: 3 +} + +.input-group > .custom-file .custom-file-input:focus { + z-index: 4 +} + +.input-group > .custom-select:not(:last-child), .input-group > .form-control:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .custom-select:not(:first-child), .input-group > .form-control:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group > .custom-file { + display: flex; + align-items: center +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label:after { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-append, .input-group-prepend { + display: flex +} + +.input-group-append .btn, .input-group-prepend .btn { + position: relative; + z-index: 2 +} + +.input-group-append .btn:focus, .input-group-prepend .btn:focus { + z-index: 3 +} + +.input-group-append .btn + .btn, .input-group-append .btn + .input-group-text, .input-group-append .input-group-text + .btn, .input-group-append .input-group-text + .input-group-text, .input-group-prepend .btn + .btn, .input-group-prepend .btn + .input-group-text, .input-group-prepend .input-group-text + .btn, .input-group-prepend .input-group-text + .input-group-text { + margin-left: -1px +} + +.input-group-prepend { + margin-right: -1px +} + +.input-group-append { + margin-left: -1px +} + +.input-group-text { + display: flex; + align-items: center; + padding: .375rem .75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: .25rem +} + +.input-group-text input[type=checkbox], .input-group-text input[type=radio] { + margin-top: 0 +} + +.input-group-lg > .custom-select, .input-group-lg > .form-control:not(textarea) { + height: calc(1.5em + 1rem + 2px) +} + +.input-group-lg > .custom-select, .input-group-lg > .form-control, .input-group-lg > .input-group-append > .btn, .input-group-lg > .input-group-append > .input-group-text, .input-group-lg > .input-group-prepend > .btn, .input-group-lg > .input-group-prepend > .input-group-text { + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +.input-group-sm > .custom-select, .input-group-sm > .form-control:not(textarea) { + height: calc(1.5em + .5rem + 2px) +} + +.input-group-sm > .custom-select, .input-group-sm > .form-control, .input-group-sm > .input-group-append > .btn, .input-group-sm > .input-group-append > .input-group-text, .input-group-sm > .input-group-prepend > .btn, .input-group-sm > .input-group-prepend > .input-group-text { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.input-group-lg > .custom-select, .input-group-sm > .custom-select { + padding-right: 1.75rem +} + +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group > .input-group-append:last-child > .input-group-text:not(:last-child), .input-group > .input-group-append:not(:last-child) > .btn, .input-group > .input-group-append:not(:last-child) > .input-group-text, .input-group > .input-group-prepend > .btn, .input-group > .input-group-prepend > .input-group-text { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .input-group-append > .btn, .input-group > .input-group-append > .input-group-text, .input-group > .input-group-prepend:first-child > .btn:not(:first-child), .input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child), .input-group > .input-group-prepend:not(:first-child) > .btn, .input-group > .input-group-prepend:not(:first-child) > .input-group-text { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem +} + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem +} + +.custom-control-input { + position: absolute; + z-index: -1; + opacity: 0 +} + +.custom-control-input:checked ~ .custom-control-label:before { + color: #fff; + border-color: #b98766; + background-color: #b98766 +} + +.custom-control-input:focus ~ .custom-control-label:before { + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-control-input:focus:not(:checked) ~ .custom-control-label:before { + border-color: #0014b7 +} + +.custom-control-input:not(:disabled):active ~ .custom-control-label:before { + color: #fff; + background-color: #0019ea; + border-color: #0019ea +} + +.custom-control-input:disabled ~ .custom-control-label { + color: #6c757d +} + +.custom-control-input:disabled ~ .custom-control-label:before { + background-color: #e9ecef +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top +} + +.custom-control-label:before { + pointer-events: none; + background-color: #fff; + border: 1px solid #adb5bd +} + +.custom-control-label:after, .custom-control-label:before { + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: "" +} + +.custom-control-label:after { + background: no-repeat 50%/50% 50% +} + +.custom-checkbox .custom-control-label:before { + border-radius: .25rem +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E") +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label:before { + border-color: #b98766; + background-color: #b98766 +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E") +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label:before { + background-color: rgba(0, 6, 55, .5) +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label:before { + background-color: rgba(0, 6, 55, .5) +} + +.custom-radio .custom-control-label:before { + border-radius: 50% +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E") +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label:before { + background-color: rgba(0, 6, 55, .5) +} + +.custom-switch { + padding-left: 2.25rem +} + +.custom-switch .custom-control-label:before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: .5rem +} + +.custom-switch .custom-control-label:after { + top: calc(.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: .5rem; + transition: transform .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .custom-switch .custom-control-label:after { + transition: none + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label:after { + background-color: #fff; + transform: translateX(.75rem) +} + +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label:before { + background-color: rgba(0, 6, 55, .5) +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + padding: .375rem 1.75rem .375rem .75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: .25rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.custom-select:focus { + border-color: #0014b7; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: .75rem; + background-image: none +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef +} + +.custom-select::-ms-expand { + display: none +} + +.custom-select-sm { + height: calc(1.5em + .5rem + 2px); + padding-top: .25rem; + padding-bottom: .25rem; + padding-left: .5rem; + font-size: .875rem +} + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: .5rem; + padding-bottom: .5rem; + padding-left: 1rem; + font-size: 1.25rem +} + +.custom-file { + display: inline-block; + margin-bottom: 0 +} + +.custom-file, .custom-file-input { + position: relative; + width: 100%; + height: calc(1.5em + .75rem + 2px) +} + +.custom-file-input { + z-index: 2; + margin: 0; + opacity: 0 +} + +.custom-file-input:focus ~ .custom-file-label { + border-color: #0014b7; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef +} + +.custom-file-input:lang(en) ~ .custom-file-label:after { + content: "Browse" +} + +.custom-file-input ~ .custom-file-label[data-browse]:after { + content: attr(data-browse) +} + +.custom-file-label { + left: 0; + z-index: 1; + height: calc(1.5em + .75rem + 2px); + font-weight: 400; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: .25rem +} + +.custom-file-label, .custom-file-label:after { + position: absolute; + top: 0; + right: 0; + padding: .375rem .75rem; + line-height: 1.5; + color: #495057 +} + +.custom-file-label:after { + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + .75rem); + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 .25rem .25rem 0 +} + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.custom-range:focus { + outline: none +} + +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.custom-range::-moz-focus-outer { + border: 0 +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -.25rem; + background-color: #b98766; + border: 0; + border-radius: 1rem; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + -webkit-appearance: none; + appearance: none +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .custom-range::-webkit-slider-thumb { + transition: none + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #0019ea +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #b98766; + border: 0; + border-radius: 1rem; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + -moz-appearance: none; + appearance: none +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .custom-range::-moz-range-thumb { + transition: none + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #0019ea +} + +.custom-range::-moz-range-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: .2rem; + margin-left: .2rem; + background-color: #b98766; + border: 0; + border-radius: 1rem; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + appearance: none +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .custom-range::-ms-thumb { + transition: none + } +} + +.custom-range::-ms-thumb:active { + background-color: #0019ea +} + +.custom-range::-ms-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: .5rem +} + +.custom-range::-ms-fill-lower, .custom-range::-ms-fill-upper { + background-color: #dee2e6; + border-radius: 1rem +} + +.custom-range::-ms-fill-upper { + margin-right: 15px +} + +.custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd +} + +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default +} + +.custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd +} + +.custom-range:disabled::-moz-range-track { + cursor: default +} + +.custom-range:disabled::-ms-thumb { + background-color: #adb5bd +} + +.custom-control-label:before, .custom-file-label, .custom-select { + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .custom-control-label:before, .custom-file-label, .custom-select { + transition: none + } +} + +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.nav-link { + display: block; + padding: .5rem 1rem +} + +.nav-link:focus, .nav-link:hover { + text-decoration: none +} + +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6 +} + +.nav-tabs .nav-item { + margin-bottom: -1px +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { + border-color: #e9ecef #e9ecef #dee2e6 +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent +} + +.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.nav-pills .nav-link { + border-radius: .25rem +} + +.nav-pills .nav-link.active, .nav-pills .show > .nav-link { + color: #fff; + background-color: #b98766 +} + +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center +} + +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center +} + +.tab-content > .tab-pane { + display: none +} + +.tab-content > .active { + display: block +} + +.navbar { + position: relative; + padding: .5rem 1rem +} + +.navbar, .navbar > .container, .navbar > .container-fluid { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between +} + +.navbar-brand { + display: inline-block; + padding-top: .3125rem; + padding-bottom: .3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap +} + +.navbar-brand:focus, .navbar-brand:hover { + text-decoration: none +} + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0 +} + +.navbar-nav .dropdown-menu { + position: static; + float: none +} + +.navbar-text { + display: inline-block; + padding-top: .5rem; + padding-bottom: .5rem +} + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center +} + +.navbar-toggler { + padding: .25rem .75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: .1875rem +} + +.navbar-toggler:focus, .navbar-toggler:hover { + text-decoration: none +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat 50%; + background-size: 100% 100% +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start + } + + .navbar-expand-sm .navbar-nav { + flex-direction: row + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { + flex-wrap: nowrap + } + + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto + } + + .navbar-expand-sm .navbar-toggler { + display: none + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start + } + + .navbar-expand-md .navbar-nav { + flex-direction: row + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { + flex-wrap: nowrap + } + + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto + } + + .navbar-expand-md .navbar-toggler { + display: none + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start + } + + .navbar-expand-lg .navbar-nav { + flex-direction: row + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { + flex-wrap: nowrap + } + + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto + } + + .navbar-expand-lg .navbar-toggler { + display: none + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start + } + + .navbar-expand-xl .navbar-nav { + flex-direction: row + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { + flex-wrap: nowrap + } + + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto + } + + .navbar-expand-xl .navbar-toggler { + display: none + } +} + +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start +} + +.navbar-expand > .container, .navbar-expand > .container-fluid { + padding-right: 0; + padding-left: 0 +} + +.navbar-expand .navbar-nav { + flex-direction: row +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem +} + +.navbar-expand > .container, .navbar-expand > .container-fluid { + flex-wrap: nowrap +} + +.navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto +} + +.navbar-expand .navbar-toggler { + display: none +} + +.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover { + color: rgba(46, 46, 46, .9) +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(46, 46, 46, .5) +} + +.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover { + color: rgba(46, 46, 46, .7) +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(46, 46, 46, .3) +} + +.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link { + color: rgba(46, 46, 46, .9) +} + +.navbar-light .navbar-toggler { + color: rgba(46, 46, 46, .5); + border-color: rgba(46, 46, 46, .1) +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") +} + +.navbar-light .navbar-text { + color: rgba(46, 46, 46, .5) +} + +.navbar-light .navbar-text a, .navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover { + color: rgba(46, 46, 46, .9) +} + +.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover { + color: #fff +} + +.navbar-dark .navbar-nav .nav-link { + color: hsla(0, 0%, 18%, .5) +} + +.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover { + color: hsla(0, 0%, 18%, .75) +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: hsla(0, 0%, 18%, .25) +} + +.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link { + color: #2e2e2e +} + +.navbar-dark .navbar-toggler { + color: rgba(46, 46, 46, 0.5); + border-color: rgba(46, 46, 46, 0.5); +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(46, 46, 46, 1)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") +} + +.navbar-dark .navbar-text { + color: hsla(0, 0%, 18%, .5) +} + +.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover { + color: #fff +} + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #303030; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, .125); + border-radius: .25rem +} + +.card > hr { + margin-right: 0; + margin-left: 0 +} + +.card > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.card-body { + flex: 1 1 auto; + padding: 1.25rem +} + +.card-title { + margin-bottom: .75rem +} + +.card-subtitle { + margin-top: -.375rem +} + +.card-subtitle, .card-text:last-child { + margin-bottom: 0 +} + +.card-link:hover { + text-decoration: none +} + +.card-link + .card-link { + margin-left: 1.25rem +} + +.card-header { + padding: .75rem 1.25rem; + margin-bottom: 0; + background-color: rgb(37 37 37); + border-bottom: 1px solid rgba(0, 0, 0, .125) +} + +.card-header:first-child { + border-radius: calc(.25rem - 1px) calc(.25rem - 1px) 0 0 +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0 +} + +.card-footer { + padding: .75rem 1.25rem; + background-color: #ffffff; + border-top: 1px solid rgba(0, 0, 0, .125) +} + +.card-footer:last-child { + border-radius: 0 0 calc(.25rem - 1px) calc(.25rem - 1px) +} + +.card-header-tabs { + margin-bottom: -.75rem; + border-bottom: 0 +} + +.card-header-pills, .card-header-tabs { + margin-right: -.625rem; + margin-left: -.625rem +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem +} + +.card-img { + width: 100%; + border-radius: calc(.25rem - 1px) +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(.25rem - 1px); + border-top-right-radius: calc(.25rem - 1px) +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(.25rem - 1px); + border-bottom-left-radius: calc(.25rem - 1px) +} + +.card-deck { + display: flex; + flex-direction: column +} + +.card-deck .card { + margin-bottom: 15px +} + +@media (min-width: 576px) { + .card-deck { + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px + } + + .card-deck .card { + display: flex; + flex: 1 0 0%; + flex-direction: column; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px + } +} + +.card-group { + display: flex; + flex-direction: column +} + +.card-group > .card { + margin-bottom: 15px +} + +@media (min-width: 576px) { + .card-group { + flex-flow: row wrap + } + + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0 + } + + .card-group > .card + .card { + margin-left: 0; + border-left: 0 + } + + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 + } + + .card-group > .card:not(:last-child) .card-header, .card-group > .card:not(:last-child) .card-img-top { + border-top-right-radius: 0 + } + + .card-group > .card:not(:last-child) .card-footer, .card-group > .card:not(:last-child) .card-img-bottom { + border-bottom-right-radius: 0 + } + + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 + } + + .card-group > .card:not(:first-child) .card-header, .card-group > .card:not(:first-child) .card-img-top { + border-top-left-radius: 0 + } + + .card-group > .card:not(:first-child) .card-footer, .card-group > .card:not(:first-child) .card-img-bottom { + border-bottom-left-radius: 0 + } +} + +.card-columns .card { + margin-bottom: .75rem +} + +@media (min-width: 576px) { + .card-columns { + -moz-column-count: 3; + column-count: 3; + -moz-column-gap: 1.25rem; + column-gap: 1.25rem; + orphans: 1; + widows: 1 + } + + .card-columns .card { + display: inline-block; + width: 100% + } +} + +.accordion > .card { + overflow: hidden +} + +.accordion > .card:not(:first-of-type) .card-header:first-child { + border-radius: 0 +} + +.accordion > .card:not(:first-of-type):not(:last-of-type) { + border-bottom: 0; + border-radius: 0 +} + +.accordion > .card:first-of-type { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.accordion > .card:last-of-type { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.accordion > .card .card-header { + margin-bottom: -1px +} + +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: .75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: .25rem +} + +.breadcrumb-item + .breadcrumb-item { + padding-left: .5rem +} + +.breadcrumb-item + .breadcrumb-item:before { + display: inline-block; + padding-right: .5rem; + color: #6c757d; + content: "/" +} + +.breadcrumb-item + .breadcrumb-item:hover:before { + text-decoration: underline; + text-decoration: none +} + +.breadcrumb-item.active { + color: #6c757d +} + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: .25rem +} + +.page-link { + position: relative; + display: block; + padding: .5rem .75rem; + margin-left: -1px; + line-height: 1.25; + color: #b98766; + background-color: #fff; + border: 1px solid #dee2e6 +} + +.page-link:hover { + z-index: 2; + color: #000; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6 +} + +.page-link:focus { + z-index: 2; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .25) +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.page-item:last-child .page-link { + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem +} + +.page-item.active .page-link { + z-index: 1; + color: #fff; + background-color: #b98766; + border-color: #b98766 +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6 +} + +.pagination-lg .page-link { + padding: .75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5 +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: .3rem; + border-bottom-left-radius: .3rem +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: .3rem; + border-bottom-right-radius: .3rem +} + +.pagination-sm .page-link { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5 +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: .2rem; + border-bottom-left-radius: .2rem +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: .2rem; + border-bottom-right-radius: .2rem +} + +.badge { + display: inline-block; + padding: .25em .4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25rem; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .badge { + transition: none + } +} + +a.badge:focus, a.badge:hover { + text-decoration: none +} + +.badge:empty { + display: none +} + +.btn .badge { + position: relative; + top: -1px +} + +.badge-pill { + padding-right: .6em; + padding-left: .6em; + border-radius: 10rem +} + +.badge-primary { + color: #fff; + background-color: #b98766 +} + +a.badge-primary:focus, a.badge-primary:hover { + color: #fff; + background-color: var(--primary); +} + +a.badge-primary.focus, a.badge-primary:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 6, 55, .5) +} + +.badge-secondary { + color: #212529; + background-color: #b55e4f +} + +a.badge-secondary:focus, a.badge-secondary:hover { + color: #212529; + background-color: #9090ea +} + +a.badge-secondary.focus, a.badge-secondary:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(187, 187, 242, .5) +} + +.badge-success { + color: #212529; + background-color: #82aa8b +} + +a.badge-success:focus, a.badge-success:hover { + color: #212529; + background-color: #35cca0 +} + +a.badge-success.focus, a.badge-success:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(94, 214, 179, .5) +} + +.badge-info { + color: #fff; + background-color: #385f84 +} + +a.badge-info:focus, a.badge-info:hover { + color: #fff; + background-color: #007ee5 +} + +a.badge-info.focus, a.badge-info:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(25, 152, 255, .5) +} + +.badge-warning { + color: #212529; + background-color: #eaaa21 +} + +a.badge-warning:focus, a.badge-warning:hover { + color: #212529; + background-color: #cc8a00 +} + +a.badge-warning.focus, a.badge-warning:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(255, 173, 0, .5) +} + +.badge-danger { + color: #fff; + background-color: #a7240e +} + +a.badge-danger:focus, a.badge-danger:hover { + color: #fff; + background-color: #c00 +} + +a.badge-danger.focus, a.badge-danger:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(255, 0, 0, .5) +} + +.badge-light { + color: #212529; + background-color: #cfd5cd +} + +a.badge-light:focus, a.badge-light:hover { + color: #212529; + background-color: #fb4e0f +} + +a.badge-light.focus, a.badge-light:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(252, 115, 65, .5) +} + +.badge-dark { + color: #fff; + background-color: #221e1e +} + +a.badge-dark:focus, a.badge-dark:hover { + color: #fff; + background-color: #070606 +} + +a.badge-dark.focus, a.badge-dark:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(34, 30, 30, .5) +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #f7f7f7; + border-radius: .3rem +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0 +} + +.alert { + position: relative; + padding: .75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: .25rem +} + +.alert-heading { + color: inherit +} + +.alert-link { + font-weight: 700 +} + +.alert-dismissible { + padding-right: 4rem +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: .75rem 1.25rem; + color: inherit +} + +.alert-primary { + color: #00031d; + background-color: #cccdd7; + border-color: #b8b9c7 +} + +.alert-primary hr { + border-top-color: #aaabbc +} + +.alert-primary .alert-link { + color: #000 +} + +.alert-secondary { + color: #61617e; + background-color: #f1f1fc; + border-color: #ececfb +} + +.alert-secondary hr { + border-top-color: #d7d7f7 +} + +.alert-secondary .alert-link { + color: #4b4b61 +} + +.alert-success { + color: #2e2e2e; + background-color: #82aa8b; + border-color: #82aa8b +} + +.alert-success hr { + border-top-color: #beefe0 +} + +.alert-success .alert-link { + color: #214c3f +} + +.alert-info { + color: #0d4f85; + background-color: #d1eaff; + border-color: #bfe2ff +} + +.alert-info hr { + border-top-color: #a6d6ff +} + +.alert-info .alert-link { + color: #083357 +} + +.alert-warning { + color: #855a00; + background-color: #ffefcc; + border-color: #ffe8b8 +} + +.alert-warning hr { + border-top-color: #ffe09f +} + +.alert-warning .alert-link { + color: #523700 +} + +.alert-danger { + color: #850000; + background-color: #fcc; + border-color: #ffb8b8 +} + +.alert-danger hr { + border-top-color: #ff9f9f +} + +.alert-danger .alert-link { + color: #520000 +} + +.alert-light { + color: #833c22; + background-color: #fee3d9; + border-color: #fed8ca +} + +.alert-light hr { + border-top-color: #fec6b1 +} + +.alert-light .alert-link { + color: #5b2917 +} + +.alert-dark { + color: #121010; + background-color: #d3d2d2; + border-color: #c1c0c0 +} + +.alert-dark hr { + border-top-color: #b4b3b3 +} + +.alert-dark .alert-link { + color: #000 +} + +@-webkit-keyframes progress-bar-stripes { + 0% { + background-position: 1rem 0 + } + to { + background-position: 0 0 + } +} + +@keyframes progress-bar-stripes { + 0% { + background-position: 1rem 0 + } + to { + background-position: 0 0 + } +} + +.progress { + display: flex; + height: 1rem; + overflow: hidden; + font-size: .75rem; + background-color: #e9ecef; + border-radius: .25rem +} + +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #b98766; + transition: width .6s ease +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .progress-bar { + transition: none + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, hsla(0, 0%, 100%, .15) 25%, transparent 0, transparent 50%, hsla(0, 0%, 100%, .15) 0, hsla(0, 0%, 100%, .15) 75%, transparent 0, transparent); + background-size: 1rem 1rem +} + +.progress-bar-animated { + -webkit-animation: progress-bar-stripes 1s linear infinite; + animation: progress-bar-stripes 1s linear infinite +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .progress-bar-animated { + -webkit-animation: none; + animation: none + } +} + +.media { + display: flex; + align-items: flex-start +} + +.media-body { + flex: 1 +} + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0 +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit +} + +.list-group-item-action:focus, .list-group-item-action:hover { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa +} + +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef +} + +.list-group-item { + position: relative; + display: block; + padding: .75rem 1.25rem; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, .125) +} + +.list-group-item:first-child { + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #b98766; + border-color: #b98766 +} + +.list-group-horizontal { + flex-direction: row +} + +.list-group-horizontal .list-group-item { + margin-right: -1px; + margin-bottom: 0 +} + +.list-group-horizontal .list-group-item:first-child { + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 +} + +.list-group-horizontal .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: 0 +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + flex-direction: row + } + + .list-group-horizontal-sm .list-group-item { + margin-right: -1px; + margin-bottom: 0 + } + + .list-group-horizontal-sm .list-group-item:first-child { + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-sm .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: 0 + } +} + +@media (min-width: 768px) { + .list-group-horizontal-md { + flex-direction: row + } + + .list-group-horizontal-md .list-group-item { + margin-right: -1px; + margin-bottom: 0 + } + + .list-group-horizontal-md .list-group-item:first-child { + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-md .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: 0 + } +} + +@media (min-width: 992px) { + .list-group-horizontal-lg { + flex-direction: row + } + + .list-group-horizontal-lg .list-group-item { + margin-right: -1px; + margin-bottom: 0 + } + + .list-group-horizontal-lg .list-group-item:first-child { + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-lg .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: 0 + } +} + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + flex-direction: row + } + + .list-group-horizontal-xl .list-group-item { + margin-right: -1px; + margin-bottom: 0 + } + + .list-group-horizontal-xl .list-group-item:first-child { + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-xl .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem; + border-bottom-left-radius: 0 + } +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0 +} + +.list-group-flush .list-group-item:last-child { + margin-bottom: -1px +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0 +} + +.list-group-flush:last-child .list-group-item:last-child { + margin-bottom: 0; + border-bottom: 0 +} + +.list-group-item-primary { + color: #00031d; + background-color: #b8b9c7 +} + +.list-group-item-primary.list-group-item-action:focus, .list-group-item-primary.list-group-item-action:hover { + color: #00031d; + background-color: #aaabbc +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #00031d; + border-color: #00031d +} + +.list-group-item-secondary { + color: #61617e; + background-color: #ececfb +} + +.list-group-item-secondary.list-group-item-action:focus, .list-group-item-secondary.list-group-item-action:hover { + color: #61617e; + background-color: #d7d7f7 +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #61617e; + border-color: #61617e +} + +.list-group-item-success { + color: #316f5d; + background-color: #d2f4ea +} + +.list-group-item-success.list-group-item-action:focus, .list-group-item-success.list-group-item-action:hover { + color: #316f5d; + background-color: #beefe0 +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #316f5d; + border-color: #316f5d +} + +.list-group-item-info { + color: #0d4f85; + background-color: #bfe2ff +} + +.list-group-item-info.list-group-item-action:focus, .list-group-item-info.list-group-item-action:hover { + color: #0d4f85; + background-color: #a6d6ff +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0d4f85; + border-color: #0d4f85 +} + +.list-group-item-warning { + color: #855a00; + background-color: #ffe8b8 +} + +.list-group-item-warning.list-group-item-action:focus, .list-group-item-warning.list-group-item-action:hover { + color: #855a00; + background-color: #ffe09f +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #855a00; + border-color: #855a00 +} + +.list-group-item-danger { + color: #850000; + background-color: #ffb8b8 +} + +.list-group-item-danger.list-group-item-action:focus, .list-group-item-danger.list-group-item-action:hover { + color: #850000; + background-color: #ff9f9f +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #850000; + border-color: #850000 +} + +.list-group-item-light { + color: #833c22; + background-color: #fed8ca +} + +.list-group-item-light.list-group-item-action:focus, .list-group-item-light.list-group-item-action:hover { + color: #833c22; + background-color: #fec6b1 +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #833c22; + border-color: #833c22 +} + +.list-group-item-dark { + color: #121010; + background-color: #c1c0c0 +} + +.list-group-item-dark.list-group-item-action:focus, .list-group-item-dark.list-group-item-action:hover { + color: #121010; + background-color: #b4b3b3 +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #121010; + border-color: #121010 +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5 +} + +.close:hover { + color: #000; + text-decoration: none +} + +.close:not(:disabled):not(.disabled):focus, .close:not(:disabled):not(.disabled):hover { + opacity: .75 +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +a.close.disabled { + pointer-events: none +} + +.toast { + max-width: 350px; + overflow: hidden; + font-size: .875rem; + background-color: hsla(0, 0%, 100%, .85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .1); + box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .1); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + opacity: 0; + border-radius: .25rem +} + +.toast:not(:last-child) { + margin-bottom: .75rem +} + +.toast.showing { + opacity: 1 +} + +.toast.show { + display: block; + opacity: 1 +} + +.toast.hide { + display: none +} + +.toast-header { + display: flex; + align-items: center; + padding: .25rem .75rem; + color: #6c757d; + background-color: hsla(0, 0%, 100%, .85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, .05) +} + +.toast-body { + padding: .75rem +} + +.modal-open { + overflow: hidden +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto +} + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0 +} + +.modal-dialog { + position: relative; + width: auto; + margin: .5rem; + pointer-events: none +} + +.modal.fade .modal-dialog { + transition: transform .3s ease-out; + transform: translateY(-50px) +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .modal.fade .modal-dialog { + transition: none + } +} + +.modal.show .modal-dialog { + transform: none +} + +.modal-dialog-scrollable { + display: flex; + max-height: calc(100% - 1rem) +} + +.modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden +} + +.modal-dialog-scrollable .modal-footer, .modal-dialog-scrollable .modal-header { + flex-shrink: 0 +} + +.modal-dialog-scrollable .modal-body { + overflow-y: auto +} + +.modal-dialog-cente#a7240e { + display: flex; + align-items: center; + min-height: calc(100% - 1rem) +} + +.modal-dialog-cente#a7240e:before { + display: block; + height: calc(100vh - 1rem); + content: "" +} + +.modal-dialog-cente#a7240e.modal-dialog-scrollable { + flex-direction: column; + justify-content: center; + height: 100% +} + +.modal-dialog-cente#a7240e.modal-dialog-scrollable .modal-content { + max-height: none +} + +.modal-dialog-cente#a7240e.modal-dialog-scrollable:before { + content: none +} + +.modal-content { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: rgb(37 37 37); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: .3rem; + outline: 0 +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000 +} + +.modal-backdrop.fade { + opacity: 0 +} + +.modal-backdrop.show { + opacity: .5 +} + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: .3rem; + border-top-right-radius: .3rem +} + +.modal-header .close { + padding: 1rem; + margin: -1rem -1rem -1rem auto +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5 +} + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: 1rem +} + +.modal-footer { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 1rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: .3rem; + border-bottom-left-radius: .3rem +} + +.modal-footer > :not(:first-child) { + margin-left: .25rem +} + +.modal-footer > :not(:last-child) { + margin-right: .25rem +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto + } + + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem) + } + + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem) + } + + .modal-dialog-cente#a7240e { + min-height: calc(100% - 3.5rem) + } + + .modal-dialog-cente#a7240e:before { + height: calc(100vh - 3.5rem) + } + + .modal-sm { + max-width: 300px + } +} + +@media (min-width: 992px) { + .modal-lg, .modal-xl { + max-width: 800px + } +} + +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: .875rem; + word-wrap: break-word; + opacity: 0 +} + +.tooltip.show { + opacity: .9 +} + +.tooltip .arrow { + position: absolute; + display: block; + width: .8rem; + height: .4rem +} + +.tooltip .arrow:before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid +} + +.bs-tooltip-auto[x-placement^=top], .bs-tooltip-top { + padding: .4rem 0 +} + +.bs-tooltip-auto[x-placement^=top] .arrow, .bs-tooltip-top .arrow { + bottom: 0 +} + +.bs-tooltip-auto[x-placement^=top] .arrow:before, .bs-tooltip-top .arrow:before { + top: 0; + border-width: .4rem .4rem 0; + border-top-color: #000 +} + +.bs-tooltip-auto[x-placement^=right], .bs-tooltip-right { + padding: 0 .4rem +} + +.bs-tooltip-auto[x-placement^=right] .arrow, .bs-tooltip-right .arrow { + left: 0; + width: .4rem; + height: .8rem +} + +.bs-tooltip-auto[x-placement^=right] .arrow:before, .bs-tooltip-right .arrow:before { + right: 0; + border-width: .4rem .4rem .4rem 0; + border-right-color: #000 +} + +.bs-tooltip-auto[x-placement^=bottom], .bs-tooltip-bottom { + padding: .4rem 0 +} + +.bs-tooltip-auto[x-placement^=bottom] .arrow, .bs-tooltip-bottom .arrow { + top: 0 +} + +.bs-tooltip-auto[x-placement^=bottom] .arrow:before, .bs-tooltip-bottom .arrow:before { + bottom: 0; + border-width: 0 .4rem .4rem; + border-bottom-color: #000 +} + +.bs-tooltip-auto[x-placement^=left], .bs-tooltip-left { + padding: 0 .4rem +} + +.bs-tooltip-auto[x-placement^=left] .arrow, .bs-tooltip-left .arrow { + right: 0; + width: .4rem; + height: .8rem +} + +.bs-tooltip-auto[x-placement^=left] .arrow:before, .bs-tooltip-left .arrow:before { + left: 0; + border-width: .4rem 0 .4rem .4rem; + border-left-color: #000 +} + +.tooltip-inner { + max-width: 200px; + padding: .25rem .5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: .25rem +} + +.popover { + top: 0; + left: 0; + z-index: 1060; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: .875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: .3rem +} + +.popover, .popover .arrow { + position: absolute; + display: block +} + +.popover .arrow { + width: 1rem; + height: .5rem; + margin: 0 .3rem +} + +.popover .arrow:after, .popover .arrow:before { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid +} + +.bs-popover-auto[x-placement^=top], .bs-popover-top { + margin-bottom: .5rem +} + +.bs-popover-auto[x-placement^=top] > .arrow, .bs-popover-top > .arrow { + bottom: calc(-.5rem + -1px) +} + +.bs-popover-auto[x-placement^=top] > .arrow:before, .bs-popover-top > .arrow:before { + bottom: 0; + border-width: .5rem .5rem 0; + border-top-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=top] > .arrow:after, .bs-popover-top > .arrow:after { + bottom: 1px; + border-width: .5rem .5rem 0; + border-top-color: #fff +} + +.bs-popover-auto[x-placement^=right], .bs-popover-right { + margin-left: .5rem +} + +.bs-popover-auto[x-placement^=right] > .arrow, .bs-popover-right > .arrow { + left: calc(-.5rem + -1px); + width: .5rem; + height: 1rem; + margin: .3rem 0 +} + +.bs-popover-auto[x-placement^=right] > .arrow:before, .bs-popover-right > .arrow:before { + left: 0; + border-width: .5rem .5rem .5rem 0; + border-right-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=right] > .arrow:after, .bs-popover-right > .arrow:after { + left: 1px; + border-width: .5rem .5rem .5rem 0; + border-right-color: #fff +} + +.bs-popover-auto[x-placement^=bottom], .bs-popover-bottom { + margin-top: .5rem +} + +.bs-popover-auto[x-placement^=bottom] > .arrow, .bs-popover-bottom > .arrow { + top: calc(-.5rem + -1px) +} + +.bs-popover-auto[x-placement^=bottom] > .arrow:before, .bs-popover-bottom > .arrow:before { + top: 0; + border-width: 0 .5rem .5rem; + border-bottom-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=bottom] > .arrow:after, .bs-popover-bottom > .arrow:after { + top: 1px; + border-width: 0 .5rem .5rem; + border-bottom-color: #fff +} + +.bs-popover-auto[x-placement^=bottom] .popover-header:before, .bs-popover-bottom .popover-header:before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7 +} + +.bs-popover-auto[x-placement^=left], .bs-popover-left { + margin-right: .5rem +} + +.bs-popover-auto[x-placement^=left] > .arrow, .bs-popover-left > .arrow { + right: calc(-.5rem + -1px); + width: .5rem; + height: 1rem; + margin: .3rem 0 +} + +.bs-popover-auto[x-placement^=left] > .arrow:before, .bs-popover-left > .arrow:before { + right: 0; + border-width: .5rem 0 .5rem .5rem; + border-left-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=left] > .arrow:after, .bs-popover-left > .arrow:after { + right: 1px; + border-width: .5rem 0 .5rem .5rem; + border-left-color: #fff +} + +.popover-header { + padding: .5rem .75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(.3rem - 1px); + border-top-right-radius: calc(.3rem - 1px) +} + +.popover-header:empty { + display: none +} + +.popover-body { + padding: .5rem .75rem; + color: #212529 +} + +.carousel { + position: relative +} + +.carousel.pointer-event { + touch-action: pan-y +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden +} + +.carousel-inner:after { + display: block; + clear: both; + content: "" +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: transform .6s ease-in-out +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .carousel-item { + transition: none + } +} + +.carousel-item-next, .carousel-item-prev, .carousel-item.active { + display: block +} + +.active.carousel-item-right, .carousel-item-next:not(.carousel-item-left) { + transform: translateX(100%) +} + +.active.carousel-item-left, .carousel-item-prev:not(.carousel-item-right) { + transform: translateX(-100%) +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none +} + +.carousel-fade .carousel-item-next.carousel-item-left, .carousel-fade .carousel-item-prev.carousel-item-right, .carousel-fade .carousel-item.active { + z-index: 1; + opacity: 1 +} + +.carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: opacity 0s .6s +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { + transition: none + } +} + +.carousel-control-next, .carousel-control-prev { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: .5; + transition: opacity .15s ease +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .carousel-control-next, .carousel-control-prev { + transition: none + } +} + +.carousel-control-next:focus, .carousel-control-next:hover, .carousel-control-prev:focus, .carousel-control-prev:hover { + color: #fff; + text-decoration: none; + outline: 0; + opacity: .9 +} + +.carousel-control-prev { + left: 0 +} + +.carousel-control-next { + right: 0 +} + +.carousel-control-next-icon, .carousel-control-prev-icon { + display: inline-block; + width: 20px; + height: 20px; + background: no-repeat 50%/100% 100% +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E") +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E") +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: flex; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none +} + +.carousel-indicators li { + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity .6s ease +} + +@media (prefers-#a7240euced-motion: #a7240euce + +) { + .carousel-indicators li { + transition: none + } +} + +.carousel-indicators .active { + opacity: 1 +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center +} + +@-webkit-keyframes spinner-border { + to { + transform: rotate(1turn) + } +} + +@keyframes spinner-border { + to { + transform: rotate(1turn) + } +} + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: .25em solid; + border-right: .25em solid transparent; + border-radius: 50%; + -webkit-animation: spinner-border .75s linear infinite; + animation: spinner-border .75s linear infinite +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: .2em +} + +@-webkit-keyframes spinner-grow { + 0% { + transform: scale(0) + } + 50% { + opacity: 1 + } +} + +@keyframes spinner-grow { + 0% { + transform: scale(0) + } + 50% { + opacity: 1 + } +} + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + -webkit-animation: spinner-grow .75s linear infinite; + animation: spinner-grow .75s linear infinite +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem +} + +.align-baseline { + vertical-align: baseline !important +} + +.align-top { + vertical-align: top !important +} + +.align-middle { + vertical-align: middle !important +} + +.align-bottom { + vertical-align: bottom !important +} + +.align-text-bottom { + vertical-align: text-bottom !important +} + +.align-text-top { + vertical-align: text-top !important +} + +/*! + * technically the wrong color but not used anywhere besides nav and this way changing nav color is supported + */ +.bg-primary { + background-color: rgb(221, 191, 134) !important; +} + +a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover { + background-color: #000004 !important +} + +.bg-secondary { + background-color: #b55e4f !important +} + +a.bg-secondary:focus, a.bg-secondary:hover, button.bg-secondary:focus, button.bg-secondary:hover { + background-color: #9090ea !important +} + +.bg-success { + background-color: #82aa8b !important +} + +a.bg-success:focus, a.bg-success:hover, button.bg-success:focus, button.bg-success:hover { + background-color: #35cca0 !important +} + +.bg-info { + background-color: #385f84 !important +} + +a.bg-info:focus, a.bg-info:hover, button.bg-info:focus, button.bg-info:hover { + background-color: #007ee5 !important +} + +.bg-warning { + background-color: #eaaa21 !important +} + +a.bg-warning:focus, a.bg-warning:hover, button.bg-warning:focus, button.bg-warning:hover { + background-color: #cc8a00 !important +} + +.bg-danger { + background-color: #a7240e !important +} + +a.bg-danger:focus, a.bg-danger:hover, button.bg-danger:focus, button.bg-danger:hover { + background-color: #c00 !important +} + +.bg-light { + background-color: #cfd5cd !important +} + +a.bg-light:focus, a.bg-light:hover, button.bg-light:focus, button.bg-light:hover { + background-color: #fb4e0f !important +} + +.bg-dark { + background-color: #221e1e !important +} + +a.bg-dark:focus, a.bg-dark:hover, button.bg-dark:focus, button.bg-dark:hover { + background-color: #070606 !important +} + +.bg-white { + background-color: #fff !important +} + +.bg-transparent { + background-color: transparent !important +} + +.border { + border: 1px solid #dee2e6 !important +} + +.border-top { + border-top: 1px solid #dee2e6 !important +} + +.border-right { + border-right: 1px solid #dee2e6 !important +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important +} + +.border-left { + border-left: 1px solid #dee2e6 !important +} + +.border-0 { + border: 0 !important +} + +.border-top-0 { + border-top: 0 !important +} + +.border-right-0 { + border-right: 0 !important +} + +.border-bottom-0 { + border-bottom: 0 !important +} + +.border-left-0 { + border-left: 0 !important +} + +.border-primary { + border-color: #b98766 !important +} + +.border-secondary { + border-color: #b55e4f !important +} + +.border-success { + border-color: #82aa8b !important +} + +.border-info { + border-color: #385f84 !important +} + +.border-warning { + border-color: #eaaa21 !important +} + +.border-danger { + border-color: #a7240e !important +} + +.border-light { + border-color: #cfd5cd !important +} + +.border-dark { + border-color: #221e1e !important +} + +.border-white { + border-color: #fff !important +} + +.rounded-sm { + border-radius: .2rem !important +} + +.rounded { + border-radius: .25rem !important +} + +.rounded-top { + border-top-left-radius: .25rem !important +} + +.rounded-right, .rounded-top { + border-top-right-radius: .25rem !important +} + +.rounded-bottom, .rounded-right { + border-bottom-right-radius: .25rem !important +} + +.rounded-bottom, .rounded-left { + border-bottom-left-radius: .25rem !important +} + +.rounded-left { + border-top-left-radius: .25rem !important +} + +.rounded-lg { + border-radius: .3rem !important +} + +.rounded-circle { + border-radius: 50% !important +} + +.rounded-pill { + border-radius: 50rem !important +} + +.rounded-0 { + border-radius: 0 !important +} + +.clearfix:after { + display: block; + clear: both; + content: "" +} + +.d-none { + display: none !important +} + +.d-inline { + display: inline !important +} + +.d-inline-block { + display: inline-block !important +} + +.d-block { + display: block !important +} + +.d-table { + display: table !important +} + +.d-table-row { + display: table-row !important +} + +.d-table-cell { + display: table-cell !important +} + +.d-flex { + display: flex !important +} + +.d-inline-flex { + display: inline-flex !important +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important + } + + .d-sm-inline { + display: inline !important + } + + .d-sm-inline-block { + display: inline-block !important + } + + .d-sm-block { + display: block !important + } + + .d-sm-table { + display: table !important + } + + .d-sm-table-row { + display: table-row !important + } + + .d-sm-table-cell { + display: table-cell !important + } + + .d-sm-flex { + display: flex !important + } + + .d-sm-inline-flex { + display: inline-flex !important + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important + } + + .d-md-inline { + display: inline !important + } + + .d-md-inline-block { + display: inline-block !important + } + + .d-md-block { + display: block !important + } + + .d-md-table { + display: table !important + } + + .d-md-table-row { + display: table-row !important + } + + .d-md-table-cell { + display: table-cell !important + } + + .d-md-flex { + display: flex !important + } + + .d-md-inline-flex { + display: inline-flex !important + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important + } + + .d-lg-inline { + display: inline !important + } + + .d-lg-inline-block { + display: inline-block !important + } + + .d-lg-block { + display: block !important + } + + .d-lg-table { + display: table !important + } + + .d-lg-table-row { + display: table-row !important + } + + .d-lg-table-cell { + display: table-cell !important + } + + .d-lg-flex { + display: flex !important + } + + .d-lg-inline-flex { + display: inline-flex !important + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important + } + + .d-xl-inline { + display: inline !important + } + + .d-xl-inline-block { + display: inline-block !important + } + + .d-xl-block { + display: block !important + } + + .d-xl-table { + display: table !important + } + + .d-xl-table-row { + display: table-row !important + } + + .d-xl-table-cell { + display: table-cell !important + } + + .d-xl-flex { + display: flex !important + } + + .d-xl-inline-flex { + display: inline-flex !important + } +} + +@media print { + .d-print-none { + display: none !important + } + + .d-print-inline { + display: inline !important + } + + .d-print-inline-block { + display: inline-block !important + } + + .d-print-block { + display: block !important + } + + .d-print-table { + display: table !important + } + + .d-print-table-row { + display: table-row !important + } + + .d-print-table-cell { + display: table-cell !important + } + + .d-print-flex { + display: flex !important + } + + .d-print-inline-flex { + display: inline-flex !important + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden +} + +.embed-responsive:before { + display: block; + content: "" +} + +.embed-responsive .embed-responsive-item, .embed-responsive embed, .embed-responsive iframe, .embed-responsive object, .embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0 +} + +.embed-responsive-21by9:before { + padding-top: 42.85714% +} + +.embed-responsive-16by9:before { + padding-top: 56.25% +} + +.embed-responsive-4by3:before { + padding-top: 75% +} + +.embed-responsive-1by1:before { + padding-top: 100% +} + +.flex-row { + flex-direction: row !important +} + +.flex-column { + flex-direction: column !important +} + +.flex-row-reverse { + flex-direction: row-reverse !important +} + +.flex-column-reverse { + flex-direction: column-reverse !important +} + +.flex-wrap { + flex-wrap: wrap !important +} + +.flex-nowrap { + flex-wrap: nowrap !important +} + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important +} + +.flex-fill { + flex: 1 1 auto !important +} + +.flex-grow-0 { + flex-grow: 0 !important +} + +.flex-grow-1 { + flex-grow: 1 !important +} + +.flex-shrink-0 { + flex-shrink: 0 !important +} + +.flex-shrink-1 { + flex-shrink: 1 !important +} + +.justify-content-start { + justify-content: flex-start !important +} + +.justify-content-end { + justify-content: flex-end !important +} + +.justify-content-center { + justify-content: center !important +} + +.justify-content-between { + justify-content: space-between !important +} + +.justify-content-around { + justify-content: space-around !important +} + +.align-items-start { + align-items: flex-start !important +} + +.align-items-end { + align-items: flex-end !important +} + +.align-items-center { + align-items: center !important +} + +.align-items-baseline { + align-items: baseline !important +} + +.align-items-stretch { + align-items: stretch !important +} + +.align-content-start { + align-content: flex-start !important +} + +.align-content-end { + align-content: flex-end !important +} + +.align-content-center { + align-content: center !important +} + +.align-content-between { + align-content: space-between !important +} + +.align-content-around { + align-content: space-around !important +} + +.align-content-stretch { + align-content: stretch !important +} + +.align-self-auto { + align-self: auto !important +} + +.align-self-start { + align-self: flex-start !important +} + +.align-self-end { + align-self: flex-end !important +} + +.align-self-center { + align-self: center !important +} + +.align-self-baseline { + align-self: baseline !important +} + +.align-self-stretch { + align-self: stretch !important +} + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important + } + + .flex-sm-column { + flex-direction: column !important + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important + } + + .flex-sm-wrap { + flex-wrap: wrap !important + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important + } + + .flex-sm-fill { + flex: 1 1 auto !important + } + + .flex-sm-grow-0 { + flex-grow: 0 !important + } + + .flex-sm-grow-1 { + flex-grow: 1 !important + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important + } + + .justify-content-sm-start { + justify-content: flex-start !important + } + + .justify-content-sm-end { + justify-content: flex-end !important + } + + .justify-content-sm-center { + justify-content: center !important + } + + .justify-content-sm-between { + justify-content: space-between !important + } + + .justify-content-sm-around { + justify-content: space-around !important + } + + .align-items-sm-start { + align-items: flex-start !important + } + + .align-items-sm-end { + align-items: flex-end !important + } + + .align-items-sm-center { + align-items: center !important + } + + .align-items-sm-baseline { + align-items: baseline !important + } + + .align-items-sm-stretch { + align-items: stretch !important + } + + .align-content-sm-start { + align-content: flex-start !important + } + + .align-content-sm-end { + align-content: flex-end !important + } + + .align-content-sm-center { + align-content: center !important + } + + .align-content-sm-between { + align-content: space-between !important + } + + .align-content-sm-around { + align-content: space-around !important + } + + .align-content-sm-stretch { + align-content: stretch !important + } + + .align-self-sm-auto { + align-self: auto !important + } + + .align-self-sm-start { + align-self: flex-start !important + } + + .align-self-sm-end { + align-self: flex-end !important + } + + .align-self-sm-center { + align-self: center !important + } + + .align-self-sm-baseline { + align-self: baseline !important + } + + .align-self-sm-stretch { + align-self: stretch !important + } +} + +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important + } + + .flex-md-column { + flex-direction: column !important + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important + } + + .flex-md-wrap { + flex-wrap: wrap !important + } + + .flex-md-nowrap { + flex-wrap: nowrap !important + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important + } + + .flex-md-fill { + flex: 1 1 auto !important + } + + .flex-md-grow-0 { + flex-grow: 0 !important + } + + .flex-md-grow-1 { + flex-grow: 1 !important + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important + } + + .justify-content-md-start { + justify-content: flex-start !important + } + + .justify-content-md-end { + justify-content: flex-end !important + } + + .justify-content-md-center { + justify-content: center !important + } + + .justify-content-md-between { + justify-content: space-between !important + } + + .justify-content-md-around { + justify-content: space-around !important + } + + .align-items-md-start { + align-items: flex-start !important + } + + .align-items-md-end { + align-items: flex-end !important + } + + .align-items-md-center { + align-items: center !important + } + + .align-items-md-baseline { + align-items: baseline !important + } + + .align-items-md-stretch { + align-items: stretch !important + } + + .align-content-md-start { + align-content: flex-start !important + } + + .align-content-md-end { + align-content: flex-end !important + } + + .align-content-md-center { + align-content: center !important + } + + .align-content-md-between { + align-content: space-between !important + } + + .align-content-md-around { + align-content: space-around !important + } + + .align-content-md-stretch { + align-content: stretch !important + } + + .align-self-md-auto { + align-self: auto !important + } + + .align-self-md-start { + align-self: flex-start !important + } + + .align-self-md-end { + align-self: flex-end !important + } + + .align-self-md-center { + align-self: center !important + } + + .align-self-md-baseline { + align-self: baseline !important + } + + .align-self-md-stretch { + align-self: stretch !important + } +} + +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important + } + + .flex-lg-column { + flex-direction: column !important + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important + } + + .flex-lg-wrap { + flex-wrap: wrap !important + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important + } + + .flex-lg-fill { + flex: 1 1 auto !important + } + + .flex-lg-grow-0 { + flex-grow: 0 !important + } + + .flex-lg-grow-1 { + flex-grow: 1 !important + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important + } + + .justify-content-lg-start { + justify-content: flex-start !important + } + + .justify-content-lg-end { + justify-content: flex-end !important + } + + .justify-content-lg-center { + justify-content: center !important + } + + .justify-content-lg-between { + justify-content: space-between !important + } + + .justify-content-lg-around { + justify-content: space-around !important + } + + .align-items-lg-start { + align-items: flex-start !important + } + + .align-items-lg-end { + align-items: flex-end !important + } + + .align-items-lg-center { + align-items: center !important + } + + .align-items-lg-baseline { + align-items: baseline !important + } + + .align-items-lg-stretch { + align-items: stretch !important + } + + .align-content-lg-start { + align-content: flex-start !important + } + + .align-content-lg-end { + align-content: flex-end !important + } + + .align-content-lg-center { + align-content: center !important + } + + .align-content-lg-between { + align-content: space-between !important + } + + .align-content-lg-around { + align-content: space-around !important + } + + .align-content-lg-stretch { + align-content: stretch !important + } + + .align-self-lg-auto { + align-self: auto !important + } + + .align-self-lg-start { + align-self: flex-start !important + } + + .align-self-lg-end { + align-self: flex-end !important + } + + .align-self-lg-center { + align-self: center !important + } + + .align-self-lg-baseline { + align-self: baseline !important + } + + .align-self-lg-stretch { + align-self: stretch !important + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important + } + + .flex-xl-column { + flex-direction: column !important + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important + } + + .flex-xl-wrap { + flex-wrap: wrap !important + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important + } + + .flex-xl-fill { + flex: 1 1 auto !important + } + + .flex-xl-grow-0 { + flex-grow: 0 !important + } + + .flex-xl-grow-1 { + flex-grow: 1 !important + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important + } + + .justify-content-xl-start { + justify-content: flex-start !important + } + + .justify-content-xl-end { + justify-content: flex-end !important + } + + .justify-content-xl-center { + justify-content: center !important + } + + .justify-content-xl-between { + justify-content: space-between !important + } + + .justify-content-xl-around { + justify-content: space-around !important + } + + .align-items-xl-start { + align-items: flex-start !important + } + + .align-items-xl-end { + align-items: flex-end !important + } + + .align-items-xl-center { + align-items: center !important + } + + .align-items-xl-baseline { + align-items: baseline !important + } + + .align-items-xl-stretch { + align-items: stretch !important + } + + .align-content-xl-start { + align-content: flex-start !important + } + + .align-content-xl-end { + align-content: flex-end !important + } + + .align-content-xl-center { + align-content: center !important + } + + .align-content-xl-between { + align-content: space-between !important + } + + .align-content-xl-around { + align-content: space-around !important + } + + .align-content-xl-stretch { + align-content: stretch !important + } + + .align-self-xl-auto { + align-self: auto !important + } + + .align-self-xl-start { + align-self: flex-start !important + } + + .align-self-xl-end { + align-self: flex-end !important + } + + .align-self-xl-center { + align-self: center !important + } + + .align-self-xl-baseline { + align-self: baseline !important + } + + .align-self-xl-stretch { + align-self: stretch !important + } +} + +.float-left { + float: left !important +} + +.float-right { + float: right !important +} + +.float-none { + float: none !important +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important + } + + .float-sm-right { + float: right !important + } + + .float-sm-none { + float: none !important + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important + } + + .float-md-right { + float: right !important + } + + .float-md-none { + float: none !important + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important + } + + .float-lg-right { + float: right !important + } + + .float-lg-none { + float: none !important + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important + } + + .float-xl-right { + float: right !important + } + + .float-xl-none { + float: none !important + } +} + +.overflow-auto { + overflow: auto !important +} + +.overflow-hidden { + overflow: hidden !important +} + +.position-static { + position: static !important +} + +.position-relative { + position: relative !important +} + +.position-absolute { + position: absolute !important +} + +.position-fixed { + position: fixed !important +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important +} + +.fixed-top { + top: 0 +} + +.fixed-bottom, .fixed-top { + position: fixed; + right: 0; + left: 0; + z-index: 1030 +} + +.fixed-bottom { + bottom: 0 +} + +@supports ((position:-webkit-sticky) or (position:sticky)) { + .sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020 + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal +} + +.shadow-sm { + box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075) !important +} + +.shadow { + box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !important +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, .175) !important +} + +.shadow-none { + box-shadow: none !important +} + +.w-25 { + width: 25% !important +} + +.w-50 { + width: 50% !important +} + +.w-75 { + width: 75% !important +} + +.w-100 { + width: 100% !important +} + +.w-auto { + width: auto !important +} + +.h-25 { + height: 25% !important +} + +.h-50 { + height: 50% !important +} + +.h-75 { + height: 75% !important +} + +.h-100 { + height: 100% !important +} + +.h-auto { + height: auto !important +} + +.mw-100 { + max-width: 100% !important +} + +.mh-100 { + max-height: 100% !important +} + +.min-vw-100 { + min-width: 100vw !important +} + +.min-vh-100 { + min-height: 100vh !important +} + +.vw-100 { + width: 100vw !important +} + +.vh-100 { + height: 100vh !important +} + +.stretched-link:after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: transparent +} + +.m-0 { + margin: 0 !important +} + +.mt-0, .my-0 { + margin-top: 0 !important +} + +.mr-0, .mx-0 { + margin-right: 0 !important +} + +.mb-0, .my-0 { + margin-bottom: 0 !important +} + +.ml-0, .mx-0 { + margin-left: 0 !important +} + +.m-1 { + margin: .25rem !important +} + +.mt-1, .my-1 { + margin-top: .25rem !important +} + +.mr-1, .mx-1 { + margin-right: .25rem !important +} + +.mb-1, .my-1 { + margin-bottom: .25rem !important +} + +.ml-1, .mx-1 { + margin-left: .25rem !important +} + +.m-2 { + margin: .5rem !important +} + +.mt-2, .my-2 { + margin-top: .5rem !important +} + +.mr-2, .mx-2 { + margin-right: .5rem !important +} + +.mb-2, .my-2 { + margin-bottom: .5rem !important +} + +.ml-2, .mx-2 { + margin-left: .5rem !important +} + +.m-3 { + margin: 1rem !important +} + +.mt-3, .my-3 { + margin-top: 1rem !important +} + +.mr-3, .mx-3 { + margin-right: 1rem !important +} + +.mb-3, .my-3 { + margin-bottom: 1rem !important +} + +.ml-3, .mx-3 { + margin-left: 1rem !important +} + +.m-4 { + margin: 1.5rem !important +} + +.mt-4, .my-4 { + margin-top: 1.5rem !important +} + +.mr-4, .mx-4 { + margin-right: 1.5rem !important +} + +.mb-4, .my-4 { + margin-bottom: 1.5rem !important +} + +.ml-4, .mx-4 { + margin-left: 1.5rem !important +} + +.m-5 { + margin: 3rem !important +} + +.mt-5, .my-5 { + margin-top: 3rem !important +} + +.mr-5, .mx-5 { + margin-right: 3rem !important +} + +.mb-5, .my-5 { + margin-bottom: 3rem !important +} + +.ml-5, .mx-5 { + margin-left: 3rem !important +} + +.p-0 { + padding: 0 !important +} + +.pt-0, .py-0 { + padding-top: 0 !important +} + +.pr-0, .px-0 { + padding-right: 0 !important +} + +.pb-0, .py-0 { + padding-bottom: 0 !important +} + +.pl-0, .px-0 { + padding-left: 0 !important +} + +.p-1 { + padding: .25rem !important +} + +.pt-1, .py-1 { + padding-top: .25rem !important +} + +.pr-1, .px-1 { + padding-right: .25rem !important +} + +.pb-1, .py-1 { + padding-bottom: .25rem !important +} + +.pl-1, .px-1 { + padding-left: .25rem !important +} + +.p-2 { + padding: .5rem !important +} + +.pt-2, .py-2 { + padding-top: .5rem !important +} + +.pr-2, .px-2 { + padding-right: .5rem !important +} + +.pb-2, .py-2 { + padding-bottom: .5rem !important +} + +.pl-2, .px-2 { + padding-left: .5rem !important +} + +.p-3 { + padding: 1rem !important +} + +.pt-3, .py-3 { + padding-top: 1rem !important +} + +.pr-3, .px-3 { + padding-right: 1rem !important +} + +.pb-3, .py-3 { + padding-bottom: 1rem !important +} + +.pl-3, .px-3 { + padding-left: 1rem !important +} + +.p-4 { + padding: 1.5rem !important +} + +.pt-4, .py-4 { + padding-top: 1.5rem !important +} + +.pr-4, .px-4 { + padding-right: 1.5rem !important +} + +.pb-4, .py-4 { + padding-bottom: 1.5rem !important +} + +.pl-4, .px-4 { + padding-left: 1.5rem !important +} + +.p-5 { + padding: 3rem !important +} + +.pt-5, .py-5 { + padding-top: 3rem !important +} + +.pr-5, .px-5 { + padding-right: 3rem !important +} + +.pb-5, .py-5 { + padding-bottom: 3rem !important +} + +.pl-5, .px-5 { + padding-left: 3rem !important +} + +.m-n1 { + margin: -.25rem !important +} + +.mt-n1, .my-n1 { + margin-top: -.25rem !important +} + +.mr-n1, .mx-n1 { + margin-right: -.25rem !important +} + +.mb-n1, .my-n1 { + margin-bottom: -.25rem !important +} + +.ml-n1, .mx-n1 { + margin-left: -.25rem !important +} + +.m-n2 { + margin: -.5rem !important +} + +.mt-n2, .my-n2 { + margin-top: -.5rem !important +} + +.mr-n2, .mx-n2 { + margin-right: -.5rem !important +} + +.mb-n2, .my-n2 { + margin-bottom: -.5rem !important +} + +.ml-n2, .mx-n2 { + margin-left: -.5rem !important +} + +.m-n3 { + margin: -1rem !important +} + +.mt-n3, .my-n3 { + margin-top: -1rem !important +} + +.mr-n3, .mx-n3 { + margin-right: -1rem !important +} + +.mb-n3, .my-n3 { + margin-bottom: -1rem !important +} + +.ml-n3, .mx-n3 { + margin-left: -1rem !important +} + +.m-n4 { + margin: -1.5rem !important +} + +.mt-n4, .my-n4 { + margin-top: -1.5rem !important +} + +.mr-n4, .mx-n4 { + margin-right: -1.5rem !important +} + +.mb-n4, .my-n4 { + margin-bottom: -1.5rem !important +} + +.ml-n4, .mx-n4 { + margin-left: -1.5rem !important +} + +.m-n5 { + margin: -3rem !important +} + +.mt-n5, .my-n5 { + margin-top: -3rem !important +} + +.mr-n5, .mx-n5 { + margin-right: -3rem !important +} + +.mb-n5, .my-n5 { + margin-bottom: -3rem !important +} + +.ml-n5, .mx-n5 { + margin-left: -3rem !important +} + +.m-auto { + margin: auto !important +} + +.mt-auto, .my-auto { + margin-top: auto !important +} + +.mr-auto, .mx-auto { + margin-right: auto !important +} + +.mb-auto, .my-auto { + margin-bottom: auto !important +} + +.ml-auto, .mx-auto { + margin-left: auto !important +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important + } + + .mt-sm-0, .my-sm-0 { + margin-top: 0 !important + } + + .mr-sm-0, .mx-sm-0 { + margin-right: 0 !important + } + + .mb-sm-0, .my-sm-0 { + margin-bottom: 0 !important + } + + .ml-sm-0, .mx-sm-0 { + margin-left: 0 !important + } + + .m-sm-1 { + margin: .25rem !important + } + + .mt-sm-1, .my-sm-1 { + margin-top: .25rem !important + } + + .mr-sm-1, .mx-sm-1 { + margin-right: .25rem !important + } + + .mb-sm-1, .my-sm-1 { + margin-bottom: .25rem !important + } + + .ml-sm-1, .mx-sm-1 { + margin-left: .25rem !important + } + + .m-sm-2 { + margin: .5rem !important + } + + .mt-sm-2, .my-sm-2 { + margin-top: .5rem !important + } + + .mr-sm-2, .mx-sm-2 { + margin-right: .5rem !important + } + + .mb-sm-2, .my-sm-2 { + margin-bottom: .5rem !important + } + + .ml-sm-2, .mx-sm-2 { + margin-left: .5rem !important + } + + .m-sm-3 { + margin: 1rem !important + } + + .mt-sm-3, .my-sm-3 { + margin-top: 1rem !important + } + + .mr-sm-3, .mx-sm-3 { + margin-right: 1rem !important + } + + .mb-sm-3, .my-sm-3 { + margin-bottom: 1rem !important + } + + .ml-sm-3, .mx-sm-3 { + margin-left: 1rem !important + } + + .m-sm-4 { + margin: 1.5rem !important + } + + .mt-sm-4, .my-sm-4 { + margin-top: 1.5rem !important + } + + .mr-sm-4, .mx-sm-4 { + margin-right: 1.5rem !important + } + + .mb-sm-4, .my-sm-4 { + margin-bottom: 1.5rem !important + } + + .ml-sm-4, .mx-sm-4 { + margin-left: 1.5rem !important + } + + .m-sm-5 { + margin: 3rem !important + } + + .mt-sm-5, .my-sm-5 { + margin-top: 3rem !important + } + + .mr-sm-5, .mx-sm-5 { + margin-right: 3rem !important + } + + .mb-sm-5, .my-sm-5 { + margin-bottom: 3rem !important + } + + .ml-sm-5, .mx-sm-5 { + margin-left: 3rem !important + } + + .p-sm-0 { + padding: 0 !important + } + + .pt-sm-0, .py-sm-0 { + padding-top: 0 !important + } + + .pr-sm-0, .px-sm-0 { + padding-right: 0 !important + } + + .pb-sm-0, .py-sm-0 { + padding-bottom: 0 !important + } + + .pl-sm-0, .px-sm-0 { + padding-left: 0 !important + } + + .p-sm-1 { + padding: .25rem !important + } + + .pt-sm-1, .py-sm-1 { + padding-top: .25rem !important + } + + .pr-sm-1, .px-sm-1 { + padding-right: .25rem !important + } + + .pb-sm-1, .py-sm-1 { + padding-bottom: .25rem !important + } + + .pl-sm-1, .px-sm-1 { + padding-left: .25rem !important + } + + .p-sm-2 { + padding: .5rem !important + } + + .pt-sm-2, .py-sm-2 { + padding-top: .5rem !important + } + + .pr-sm-2, .px-sm-2 { + padding-right: .5rem !important + } + + .pb-sm-2, .py-sm-2 { + padding-bottom: .5rem !important + } + + .pl-sm-2, .px-sm-2 { + padding-left: .5rem !important + } + + .p-sm-3 { + padding: 1rem !important + } + + .pt-sm-3, .py-sm-3 { + padding-top: 1rem !important + } + + .pr-sm-3, .px-sm-3 { + padding-right: 1rem !important + } + + .pb-sm-3, .py-sm-3 { + padding-bottom: 1rem !important + } + + .pl-sm-3, .px-sm-3 { + padding-left: 1rem !important + } + + .p-sm-4 { + padding: 1.5rem !important + } + + .pt-sm-4, .py-sm-4 { + padding-top: 1.5rem !important + } + + .pr-sm-4, .px-sm-4 { + padding-right: 1.5rem !important + } + + .pb-sm-4, .py-sm-4 { + padding-bottom: 1.5rem !important + } + + .pl-sm-4, .px-sm-4 { + padding-left: 1.5rem !important + } + + .p-sm-5 { + padding: 3rem !important + } + + .pt-sm-5, .py-sm-5 { + padding-top: 3rem !important + } + + .pr-sm-5, .px-sm-5 { + padding-right: 3rem !important + } + + .pb-sm-5, .py-sm-5 { + padding-bottom: 3rem !important + } + + .pl-sm-5, .px-sm-5 { + padding-left: 3rem !important + } + + .m-sm-n1 { + margin: -.25rem !important + } + + .mt-sm-n1, .my-sm-n1 { + margin-top: -.25rem !important + } + + .mr-sm-n1, .mx-sm-n1 { + margin-right: -.25rem !important + } + + .mb-sm-n1, .my-sm-n1 { + margin-bottom: -.25rem !important + } + + .ml-sm-n1, .mx-sm-n1 { + margin-left: -.25rem !important + } + + .m-sm-n2 { + margin: -.5rem !important + } + + .mt-sm-n2, .my-sm-n2 { + margin-top: -.5rem !important + } + + .mr-sm-n2, .mx-sm-n2 { + margin-right: -.5rem !important + } + + .mb-sm-n2, .my-sm-n2 { + margin-bottom: -.5rem !important + } + + .ml-sm-n2, .mx-sm-n2 { + margin-left: -.5rem !important + } + + .m-sm-n3 { + margin: -1rem !important + } + + .mt-sm-n3, .my-sm-n3 { + margin-top: -1rem !important + } + + .mr-sm-n3, .mx-sm-n3 { + margin-right: -1rem !important + } + + .mb-sm-n3, .my-sm-n3 { + margin-bottom: -1rem !important + } + + .ml-sm-n3, .mx-sm-n3 { + margin-left: -1rem !important + } + + .m-sm-n4 { + margin: -1.5rem !important + } + + .mt-sm-n4, .my-sm-n4 { + margin-top: -1.5rem !important + } + + .mr-sm-n4, .mx-sm-n4 { + margin-right: -1.5rem !important + } + + .mb-sm-n4, .my-sm-n4 { + margin-bottom: -1.5rem !important + } + + .ml-sm-n4, .mx-sm-n4 { + margin-left: -1.5rem !important + } + + .m-sm-n5 { + margin: -3rem !important + } + + .mt-sm-n5, .my-sm-n5 { + margin-top: -3rem !important + } + + .mr-sm-n5, .mx-sm-n5 { + margin-right: -3rem !important + } + + .mb-sm-n5, .my-sm-n5 { + margin-bottom: -3rem !important + } + + .ml-sm-n5, .mx-sm-n5 { + margin-left: -3rem !important + } + + .m-sm-auto { + margin: auto !important + } + + .mt-sm-auto, .my-sm-auto { + margin-top: auto !important + } + + .mr-sm-auto, .mx-sm-auto { + margin-right: auto !important + } + + .mb-sm-auto, .my-sm-auto { + margin-bottom: auto !important + } + + .ml-sm-auto, .mx-sm-auto { + margin-left: auto !important + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important + } + + .mt-md-0, .my-md-0 { + margin-top: 0 !important + } + + .mr-md-0, .mx-md-0 { + margin-right: 0 !important + } + + .mb-md-0, .my-md-0 { + margin-bottom: 0 !important + } + + .ml-md-0, .mx-md-0 { + margin-left: 0 !important + } + + .m-md-1 { + margin: .25rem !important + } + + .mt-md-1, .my-md-1 { + margin-top: .25rem !important + } + + .mr-md-1, .mx-md-1 { + margin-right: .25rem !important + } + + .mb-md-1, .my-md-1 { + margin-bottom: .25rem !important + } + + .ml-md-1, .mx-md-1 { + margin-left: .25rem !important + } + + .m-md-2 { + margin: .5rem !important + } + + .mt-md-2, .my-md-2 { + margin-top: .5rem !important + } + + .mr-md-2, .mx-md-2 { + margin-right: .5rem !important + } + + .mb-md-2, .my-md-2 { + margin-bottom: .5rem !important + } + + .ml-md-2, .mx-md-2 { + margin-left: .5rem !important + } + + .m-md-3 { + margin: 1rem !important + } + + .mt-md-3, .my-md-3 { + margin-top: 1rem !important + } + + .mr-md-3, .mx-md-3 { + margin-right: 1rem !important + } + + .mb-md-3, .my-md-3 { + margin-bottom: 1rem !important + } + + .ml-md-3, .mx-md-3 { + margin-left: 1rem !important + } + + .m-md-4 { + margin: 1.5rem !important + } + + .mt-md-4, .my-md-4 { + margin-top: 1.5rem !important + } + + .mr-md-4, .mx-md-4 { + margin-right: 1.5rem !important + } + + .mb-md-4, .my-md-4 { + margin-bottom: 1.5rem !important + } + + .ml-md-4, .mx-md-4 { + margin-left: 1.5rem !important + } + + .m-md-5 { + margin: 3rem !important + } + + .mt-md-5, .my-md-5 { + margin-top: 3rem !important + } + + .mr-md-5, .mx-md-5 { + margin-right: 3rem !important + } + + .mb-md-5, .my-md-5 { + margin-bottom: 3rem !important + } + + .ml-md-5, .mx-md-5 { + margin-left: 3rem !important + } + + .p-md-0 { + padding: 0 !important + } + + .pt-md-0, .py-md-0 { + padding-top: 0 !important + } + + .pr-md-0, .px-md-0 { + padding-right: 0 !important + } + + .pb-md-0, .py-md-0 { + padding-bottom: 0 !important + } + + .pl-md-0, .px-md-0 { + padding-left: 0 !important + } + + .p-md-1 { + padding: .25rem !important + } + + .pt-md-1, .py-md-1 { + padding-top: .25rem !important + } + + .pr-md-1, .px-md-1 { + padding-right: .25rem !important + } + + .pb-md-1, .py-md-1 { + padding-bottom: .25rem !important + } + + .pl-md-1, .px-md-1 { + padding-left: .25rem !important + } + + .p-md-2 { + padding: .5rem !important + } + + .pt-md-2, .py-md-2 { + padding-top: .5rem !important + } + + .pr-md-2, .px-md-2 { + padding-right: .5rem !important + } + + .pb-md-2, .py-md-2 { + padding-bottom: .5rem !important + } + + .pl-md-2, .px-md-2 { + padding-left: .5rem !important + } + + .p-md-3 { + padding: 1rem !important + } + + .pt-md-3, .py-md-3 { + padding-top: 1rem !important + } + + .pr-md-3, .px-md-3 { + padding-right: 1rem !important + } + + .pb-md-3, .py-md-3 { + padding-bottom: 1rem !important + } + + .pl-md-3, .px-md-3 { + padding-left: 1rem !important + } + + .p-md-4 { + padding: 1.5rem !important + } + + .pt-md-4, .py-md-4 { + padding-top: 1.5rem !important + } + + .pr-md-4, .px-md-4 { + padding-right: 1.5rem !important + } + + .pb-md-4, .py-md-4 { + padding-bottom: 1.5rem !important + } + + .pl-md-4, .px-md-4 { + padding-left: 1.5rem !important + } + + .p-md-5 { + padding: 3rem !important + } + + .pt-md-5, .py-md-5 { + padding-top: 3rem !important + } + + .pr-md-5, .px-md-5 { + padding-right: 3rem !important + } + + .pb-md-5, .py-md-5 { + padding-bottom: 3rem !important + } + + .pl-md-5, .px-md-5 { + padding-left: 3rem !important + } + + .m-md-n1 { + margin: -.25rem !important + } + + .mt-md-n1, .my-md-n1 { + margin-top: -.25rem !important + } + + .mr-md-n1, .mx-md-n1 { + margin-right: -.25rem !important + } + + .mb-md-n1, .my-md-n1 { + margin-bottom: -.25rem !important + } + + .ml-md-n1, .mx-md-n1 { + margin-left: -.25rem !important + } + + .m-md-n2 { + margin: -.5rem !important + } + + .mt-md-n2, .my-md-n2 { + margin-top: -.5rem !important + } + + .mr-md-n2, .mx-md-n2 { + margin-right: -.5rem !important + } + + .mb-md-n2, .my-md-n2 { + margin-bottom: -.5rem !important + } + + .ml-md-n2, .mx-md-n2 { + margin-left: -.5rem !important + } + + .m-md-n3 { + margin: -1rem !important + } + + .mt-md-n3, .my-md-n3 { + margin-top: -1rem !important + } + + .mr-md-n3, .mx-md-n3 { + margin-right: -1rem !important + } + + .mb-md-n3, .my-md-n3 { + margin-bottom: -1rem !important + } + + .ml-md-n3, .mx-md-n3 { + margin-left: -1rem !important + } + + .m-md-n4 { + margin: -1.5rem !important + } + + .mt-md-n4, .my-md-n4 { + margin-top: -1.5rem !important + } + + .mr-md-n4, .mx-md-n4 { + margin-right: -1.5rem !important + } + + .mb-md-n4, .my-md-n4 { + margin-bottom: -1.5rem !important + } + + .ml-md-n4, .mx-md-n4 { + margin-left: -1.5rem !important + } + + .m-md-n5 { + margin: -3rem !important + } + + .mt-md-n5, .my-md-n5 { + margin-top: -3rem !important + } + + .mr-md-n5, .mx-md-n5 { + margin-right: -3rem !important + } + + .mb-md-n5, .my-md-n5 { + margin-bottom: -3rem !important + } + + .ml-md-n5, .mx-md-n5 { + margin-left: -3rem !important + } + + .m-md-auto { + margin: auto !important + } + + .mt-md-auto, .my-md-auto { + margin-top: auto !important + } + + .mr-md-auto, .mx-md-auto { + margin-right: auto !important + } + + .mb-md-auto, .my-md-auto { + margin-bottom: auto !important + } + + .ml-md-auto, .mx-md-auto { + margin-left: auto !important + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important + } + + .mt-lg-0, .my-lg-0 { + margin-top: 0 !important + } + + .mr-lg-0, .mx-lg-0 { + margin-right: 0 !important + } + + .mb-lg-0, .my-lg-0 { + margin-bottom: 0 !important + } + + .ml-lg-0, .mx-lg-0 { + margin-left: 0 !important + } + + .m-lg-1 { + margin: .25rem !important + } + + .mt-lg-1, .my-lg-1 { + margin-top: .25rem !important + } + + .mr-lg-1, .mx-lg-1 { + margin-right: .25rem !important + } + + .mb-lg-1, .my-lg-1 { + margin-bottom: .25rem !important + } + + .ml-lg-1, .mx-lg-1 { + margin-left: .25rem !important + } + + .m-lg-2 { + margin: .5rem !important + } + + .mt-lg-2, .my-lg-2 { + margin-top: .5rem !important + } + + .mr-lg-2, .mx-lg-2 { + margin-right: .5rem !important + } + + .mb-lg-2, .my-lg-2 { + margin-bottom: .5rem !important + } + + .ml-lg-2, .mx-lg-2 { + margin-left: .5rem !important + } + + .m-lg-3 { + margin: 1rem !important + } + + .mt-lg-3, .my-lg-3 { + margin-top: 1rem !important + } + + .mr-lg-3, .mx-lg-3 { + margin-right: 1rem !important + } + + .mb-lg-3, .my-lg-3 { + margin-bottom: 1rem !important + } + + .ml-lg-3, .mx-lg-3 { + margin-left: 1rem !important + } + + .m-lg-4 { + margin: 1.5rem !important + } + + .mt-lg-4, .my-lg-4 { + margin-top: 1.5rem !important + } + + .mr-lg-4, .mx-lg-4 { + margin-right: 1.5rem !important + } + + .mb-lg-4, .my-lg-4 { + margin-bottom: 1.5rem !important + } + + .ml-lg-4, .mx-lg-4 { + margin-left: 1.5rem !important + } + + .m-lg-5 { + margin: 3rem !important + } + + .mt-lg-5, .my-lg-5 { + margin-top: 3rem !important + } + + .mr-lg-5, .mx-lg-5 { + margin-right: 3rem !important + } + + .mb-lg-5, .my-lg-5 { + margin-bottom: 3rem !important + } + + .ml-lg-5, .mx-lg-5 { + margin-left: 3rem !important + } + + .p-lg-0 { + padding: 0 !important + } + + .pt-lg-0, .py-lg-0 { + padding-top: 0 !important + } + + .pr-lg-0, .px-lg-0 { + padding-right: 0 !important + } + + .pb-lg-0, .py-lg-0 { + padding-bottom: 0 !important + } + + .pl-lg-0, .px-lg-0 { + padding-left: 0 !important + } + + .p-lg-1 { + padding: .25rem !important + } + + .pt-lg-1, .py-lg-1 { + padding-top: .25rem !important + } + + .pr-lg-1, .px-lg-1 { + padding-right: .25rem !important + } + + .pb-lg-1, .py-lg-1 { + padding-bottom: .25rem !important + } + + .pl-lg-1, .px-lg-1 { + padding-left: .25rem !important + } + + .p-lg-2 { + padding: .5rem !important + } + + .pt-lg-2, .py-lg-2 { + padding-top: .5rem !important + } + + .pr-lg-2, .px-lg-2 { + padding-right: .5rem !important + } + + .pb-lg-2, .py-lg-2 { + padding-bottom: .5rem !important + } + + .pl-lg-2, .px-lg-2 { + padding-left: .5rem !important + } + + .p-lg-3 { + padding: 1rem !important + } + + .pt-lg-3, .py-lg-3 { + padding-top: 1rem !important + } + + .pr-lg-3, .px-lg-3 { + padding-right: 1rem !important + } + + .pb-lg-3, .py-lg-3 { + padding-bottom: 1rem !important + } + + .pl-lg-3, .px-lg-3 { + padding-left: 1rem !important + } + + .p-lg-4 { + padding: 1.5rem !important + } + + .pt-lg-4, .py-lg-4 { + padding-top: 1.5rem !important + } + + .pr-lg-4, .px-lg-4 { + padding-right: 1.5rem !important + } + + .pb-lg-4, .py-lg-4 { + padding-bottom: 1.5rem !important + } + + .pl-lg-4, .px-lg-4 { + padding-left: 1.5rem !important + } + + .p-lg-5 { + padding: 3rem !important + } + + .pt-lg-5, .py-lg-5 { + padding-top: 3rem !important + } + + .pr-lg-5, .px-lg-5 { + padding-right: 3rem !important + } + + .pb-lg-5, .py-lg-5 { + padding-bottom: 3rem !important + } + + .pl-lg-5, .px-lg-5 { + padding-left: 3rem !important + } + + .m-lg-n1 { + margin: -.25rem !important + } + + .mt-lg-n1, .my-lg-n1 { + margin-top: -.25rem !important + } + + .mr-lg-n1, .mx-lg-n1 { + margin-right: -.25rem !important + } + + .mb-lg-n1, .my-lg-n1 { + margin-bottom: -.25rem !important + } + + .ml-lg-n1, .mx-lg-n1 { + margin-left: -.25rem !important + } + + .m-lg-n2 { + margin: -.5rem !important + } + + .mt-lg-n2, .my-lg-n2 { + margin-top: -.5rem !important + } + + .mr-lg-n2, .mx-lg-n2 { + margin-right: -.5rem !important + } + + .mb-lg-n2, .my-lg-n2 { + margin-bottom: -.5rem !important + } + + .ml-lg-n2, .mx-lg-n2 { + margin-left: -.5rem !important + } + + .m-lg-n3 { + margin: -1rem !important + } + + .mt-lg-n3, .my-lg-n3 { + margin-top: -1rem !important + } + + .mr-lg-n3, .mx-lg-n3 { + margin-right: -1rem !important + } + + .mb-lg-n3, .my-lg-n3 { + margin-bottom: -1rem !important + } + + .ml-lg-n3, .mx-lg-n3 { + margin-left: -1rem !important + } + + .m-lg-n4 { + margin: -1.5rem !important + } + + .mt-lg-n4, .my-lg-n4 { + margin-top: -1.5rem !important + } + + .mr-lg-n4, .mx-lg-n4 { + margin-right: -1.5rem !important + } + + .mb-lg-n4, .my-lg-n4 { + margin-bottom: -1.5rem !important + } + + .ml-lg-n4, .mx-lg-n4 { + margin-left: -1.5rem !important + } + + .m-lg-n5 { + margin: -3rem !important + } + + .mt-lg-n5, .my-lg-n5 { + margin-top: -3rem !important + } + + .mr-lg-n5, .mx-lg-n5 { + margin-right: -3rem !important + } + + .mb-lg-n5, .my-lg-n5 { + margin-bottom: -3rem !important + } + + .ml-lg-n5, .mx-lg-n5 { + margin-left: -3rem !important + } + + .m-lg-auto { + margin: auto !important + } + + .mt-lg-auto, .my-lg-auto { + margin-top: auto !important + } + + .mr-lg-auto, .mx-lg-auto { + margin-right: auto !important + } + + .mb-lg-auto, .my-lg-auto { + margin-bottom: auto !important + } + + .ml-lg-auto, .mx-lg-auto { + margin-left: auto !important + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important + } + + .mt-xl-0, .my-xl-0 { + margin-top: 0 !important + } + + .mr-xl-0, .mx-xl-0 { + margin-right: 0 !important + } + + .mb-xl-0, .my-xl-0 { + margin-bottom: 0 !important + } + + .ml-xl-0, .mx-xl-0 { + margin-left: 0 !important + } + + .m-xl-1 { + margin: .25rem !important + } + + .mt-xl-1, .my-xl-1 { + margin-top: .25rem !important + } + + .mr-xl-1, .mx-xl-1 { + margin-right: .25rem !important + } + + .mb-xl-1, .my-xl-1 { + margin-bottom: .25rem !important + } + + .ml-xl-1, .mx-xl-1 { + margin-left: .25rem !important + } + + .m-xl-2 { + margin: .5rem !important + } + + .mt-xl-2, .my-xl-2 { + margin-top: .5rem !important + } + + .mr-xl-2, .mx-xl-2 { + margin-right: .5rem !important + } + + .mb-xl-2, .my-xl-2 { + margin-bottom: .5rem !important + } + + .ml-xl-2, .mx-xl-2 { + margin-left: .5rem !important + } + + .m-xl-3 { + margin: 1rem !important + } + + .mt-xl-3, .my-xl-3 { + margin-top: 1rem !important + } + + .mr-xl-3, .mx-xl-3 { + margin-right: 1rem !important + } + + .mb-xl-3, .my-xl-3 { + margin-bottom: 1rem !important + } + + .ml-xl-3, .mx-xl-3 { + margin-left: 1rem !important + } + + .m-xl-4 { + margin: 1.5rem !important + } + + .mt-xl-4, .my-xl-4 { + margin-top: 1.5rem !important + } + + .mr-xl-4, .mx-xl-4 { + margin-right: 1.5rem !important + } + + .mb-xl-4, .my-xl-4 { + margin-bottom: 1.5rem !important + } + + .ml-xl-4, .mx-xl-4 { + margin-left: 1.5rem !important + } + + .m-xl-5 { + margin: 3rem !important + } + + .mt-xl-5, .my-xl-5 { + margin-top: 3rem !important + } + + .mr-xl-5, .mx-xl-5 { + margin-right: 3rem !important + } + + .mb-xl-5, .my-xl-5 { + margin-bottom: 3rem !important + } + + .ml-xl-5, .mx-xl-5 { + margin-left: 3rem !important + } + + .p-xl-0 { + padding: 0 !important + } + + .pt-xl-0, .py-xl-0 { + padding-top: 0 !important + } + + .pr-xl-0, .px-xl-0 { + padding-right: 0 !important + } + + .pb-xl-0, .py-xl-0 { + padding-bottom: 0 !important + } + + .pl-xl-0, .px-xl-0 { + padding-left: 0 !important + } + + .p-xl-1 { + padding: .25rem !important + } + + .pt-xl-1, .py-xl-1 { + padding-top: .25rem !important + } + + .pr-xl-1, .px-xl-1 { + padding-right: .25rem !important + } + + .pb-xl-1, .py-xl-1 { + padding-bottom: .25rem !important + } + + .pl-xl-1, .px-xl-1 { + padding-left: .25rem !important + } + + .p-xl-2 { + padding: .5rem !important + } + + .pt-xl-2, .py-xl-2 { + padding-top: .5rem !important + } + + .pr-xl-2, .px-xl-2 { + padding-right: .5rem !important + } + + .pb-xl-2, .py-xl-2 { + padding-bottom: .5rem !important + } + + .pl-xl-2, .px-xl-2 { + padding-left: .5rem !important + } + + .p-xl-3 { + padding: 1rem !important + } + + .pt-xl-3, .py-xl-3 { + padding-top: 1rem !important + } + + .pr-xl-3, .px-xl-3 { + padding-right: 1rem !important + } + + .pb-xl-3, .py-xl-3 { + padding-bottom: 1rem !important + } + + .pl-xl-3, .px-xl-3 { + padding-left: 1rem !important + } + + .p-xl-4 { + padding: 1.5rem !important + } + + .pt-xl-4, .py-xl-4 { + padding-top: 1.5rem !important + } + + .pr-xl-4, .px-xl-4 { + padding-right: 1.5rem !important + } + + .pb-xl-4, .py-xl-4 { + padding-bottom: 1.5rem !important + } + + .pl-xl-4, .px-xl-4 { + padding-left: 1.5rem !important + } + + .p-xl-5 { + padding: 3rem !important + } + + .pt-xl-5, .py-xl-5 { + padding-top: 3rem !important + } + + .pr-xl-5, .px-xl-5 { + padding-right: 3rem !important + } + + .pb-xl-5, .py-xl-5 { + padding-bottom: 3rem !important + } + + .pl-xl-5, .px-xl-5 { + padding-left: 3rem !important + } + + .m-xl-n1 { + margin: -.25rem !important + } + + .mt-xl-n1, .my-xl-n1 { + margin-top: -.25rem !important + } + + .mr-xl-n1, .mx-xl-n1 { + margin-right: -.25rem !important + } + + .mb-xl-n1, .my-xl-n1 { + margin-bottom: -.25rem !important + } + + .ml-xl-n1, .mx-xl-n1 { + margin-left: -.25rem !important + } + + .m-xl-n2 { + margin: -.5rem !important + } + + .mt-xl-n2, .my-xl-n2 { + margin-top: -.5rem !important + } + + .mr-xl-n2, .mx-xl-n2 { + margin-right: -.5rem !important + } + + .mb-xl-n2, .my-xl-n2 { + margin-bottom: -.5rem !important + } + + .ml-xl-n2, .mx-xl-n2 { + margin-left: -.5rem !important + } + + .m-xl-n3 { + margin: -1rem !important + } + + .mt-xl-n3, .my-xl-n3 { + margin-top: -1rem !important + } + + .mr-xl-n3, .mx-xl-n3 { + margin-right: -1rem !important + } + + .mb-xl-n3, .my-xl-n3 { + margin-bottom: -1rem !important + } + + .ml-xl-n3, .mx-xl-n3 { + margin-left: -1rem !important + } + + .m-xl-n4 { + margin: -1.5rem !important + } + + .mt-xl-n4, .my-xl-n4 { + margin-top: -1.5rem !important + } + + .mr-xl-n4, .mx-xl-n4 { + margin-right: -1.5rem !important + } + + .mb-xl-n4, .my-xl-n4 { + margin-bottom: -1.5rem !important + } + + .ml-xl-n4, .mx-xl-n4 { + margin-left: -1.5rem !important + } + + .m-xl-n5 { + margin: -3rem !important + } + + .mt-xl-n5, .my-xl-n5 { + margin-top: -3rem !important + } + + .mr-xl-n5, .mx-xl-n5 { + margin-right: -3rem !important + } + + .mb-xl-n5, .my-xl-n5 { + margin-bottom: -3rem !important + } + + .ml-xl-n5, .mx-xl-n5 { + margin-left: -3rem !important + } + + .m-xl-auto { + margin: auto !important + } + + .mt-xl-auto, .my-xl-auto { + margin-top: auto !important + } + + .mr-xl-auto, .mx-xl-auto { + margin-right: auto !important + } + + .mb-xl-auto, .my-xl-auto { + margin-bottom: auto !important + } + + .ml-xl-auto, .mx-xl-auto { + margin-left: auto !important + } +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace !important +} + +.text-justify { + text-align: justify !important +} + +.text-wrap { + white-space: normal !important +} + +.text-nowrap { + white-space: nowrap !important +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap +} + +.text-left { + text-align: left !important +} + +.text-right { + text-align: right !important +} + +.text-center { + text-align: center !important +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important + } + + .text-sm-right { + text-align: right !important + } + + .text-sm-center { + text-align: center !important + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important + } + + .text-md-right { + text-align: right !important + } + + .text-md-center { + text-align: center !important + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important + } + + .text-lg-right { + text-align: right !important + } + + .text-lg-center { + text-align: center !important + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important + } + + .text-xl-right { + text-align: right !important + } + + .text-xl-center { + text-align: center !important + } +} + +.text-lowercase { + text-transform: lowercase !important +} + +.text-uppercase { + text-transform: uppercase !important +} + +.text-capitalize { + text-transform: capitalize !important +} + +.font-weight-light { + font-weight: 300 !important +} + +.font-weight-lighter { + font-weight: lighter !important +} + +.font-weight-normal { + font-weight: 400 !important +} + +.font-weight-bold { + font-weight: 700 !important +} + +.font-weight-bolder { + font-weight: bolder !important +} + +.font-italic { + font-style: italic !important +} + +.text-white { + color: #fff !important +} + +.text-primary { + color: #b98766 !important +} + +a.text-primary:focus, a.text-primary:hover { + color: #000 !important +} + +.text-secondary { + color: #b55e4f !important +} + +a.text-secondary:focus, a.text-secondary:hover { + color: #7b7be6 !important +} + +.text-success { + color: #82aa8b !important +} + +a.text-success:focus, a.text-success:hover { + color: #2fb990 !important +} + +.text-info { + color: #385f84 !important +} + +a.text-info:focus, a.text-info:hover { + color: #0070cc !important +} + +.text-warning { + color: #eaaa21 !important +} + +a.text-warning:focus, a.text-warning:hover { + color: #b37900 !important +} + +.text-danger { + color: #a7240e !important +} + +a.text-danger:focus, a.text-danger:hover { + color: #b30000 !important +} + +.text-light { + color: #cfd5cd !important +} + +a.text-light:focus, a.text-light:hover { + color: #ed4204 !important +} + +.text-dark { + color: #fff !important +} + +a.text-dark:focus, a.text-dark:hover { + color: #000 !important +} + +.text-body { + color: #fff !important +} + +.text-muted { + color: #6e6e6e !important +} + +.text-black-50 { + color: rgba(0, 0, 0, .5) !important +} + +.text-white-50 { + color: hsla(0, 0%, 100%, .5) !important +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.text-decoration-none { + text-decoration: none !important +} + +.text-break { + word-break: break-word !important; + overflow-wrap: break-word !important +} + +.text-reset { + color: inherit !important +} + +.visible { + visibility: visible !important +} + +.invisible { + visibility: hidden !important +} + +@media print { + *, :after, :before { + text-shadow: none !important; + box-shadow: none !important; + background-color: transparent !important; + } + + a:not(.btn) { + text-decoration: underline + } + + abbr[title]:after { + content: " (" attr(title) ")" + } + + pre { + white-space: pre-wrap !important + } + + blockquote, pre { + border: 1px solid #adb5bd; + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + img, tr { + page-break-inside: avoid + } + + h2, h3, p { + orphans: 3; + widows: 3 + } + + h2, h3 { + page-break-after: avoid + } + + @page { + size: a3 + } + + .container, body { + min-width: 992px !important + } + + .navbar { + display: none + } + + .badge { + border: 1px solid #000 + } + + .table { + border-collapse: collapse !important + } + + .table td, .table th { + background-color: #fff !important + } + + .table-borde#a7240e td, .table-borde#a7240e th { + border: 1px solid #dee2e6 !important + } + + .table-dark { + color: inherit + } + + .table-dark tbody + tbody, .table-dark td, .table-dark th, .table-dark thead th { + border-color: #dee2e6 + } + + .table .thead-dark th { + color: inherit; + border-color: #dee2e6 + } +} + +h1 { + font-size: 40px +} + +@media (max-width: 756px) { + h1 { + font-size: 35px + } +} + +h2 { + font-size: 35px +} + +@media (max-width: 756px) { + h2 { + font-size: 32px + } +} + +h3 { + font-size: 30px +} + +h4 { + font-size: 25px +} + +h5 { + font-size: 20px +} + +h6 { + font-size: 17px +} + +p { + font-size: 15px +} + +.font-weight-medium { + font-weight: 500 +} + +.font-weight-semibold { + font-weight: 600 +} + +body { + font-family: Poppins, sans-serif; + margin: 0; + color: #fff +} + +body.body-top-padding { + padding-top: 167px +} + +body a:hover { + text-decoration: none +} + +body.sidebar-open .fixed-top { + background-color: transparent +} + +body.sidebar-open .fixed-top .navbar-brand-wrapper img { + display: none +} + +body.sidebar-open:before { + position: fixed; + content: ""; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 3; + background-color: rgba(0, 0, 0, .8) +} + +.banner { + background-color: #f7f8fa; + text-align: center +} + +.content-wrapper { + padding: 27px 0 +} + +.features-overview .content-header { + padding: 78px 0 48px; + text-align: center +} + +@media (max-width: 992px) { + .features-overview .content-header { + padding: 60px 0 + } +} + +.features-overview .readmore-link { + font-size: 13px; + color: #97a3be; + font-weight: 500; + transition: color .5s ease-in-out +} + +.features-overview .readmore-link:hover { + color: #a7240e +} + +.features-overview .features-width { + flex-basis: 61% +} + +@media (max-width: 767px) { + .features-overview .features-width { + flex-basis: 100% + } +} + +.features-overview .img-icons { + transition: all .4s ease-in-out +} + +.features-overview .img-icons:hover { + transform: scale(1.1) +} + +@media (max-width: 992px) { + .digital-marketing-service .flex-item:first-of-type { + order: 2 + } + + .digital-marketing-service .flex-item:nth-of-type(2) { + order: 1 + } + + .digital-marketing-service .img-digital { + text-align: center + } +} + +.case-studies .color-cards .card-contents { + position: relative; + padding: 75px 0 +} + +.case-studies .color-cards .card-details p { + color: #6e6e6e; + font-weight: 500 +} + +.case-studies .color-cards:hover .card-details, .case-studies .color-cards:hover .card-details p { + color: rgba(0, 51, 204, .8) +} + +.case-studies .color-cards:hover .card-desc-box { + opacity: 1 +} + +.case-studies .color-cards .btn { + padding: 6px 14px; + font-size: 12px +} + +.case-studies .bg-violet { + background: #b55e4f +} + +.case-studies .case-studies-card-img { + height: 106px +} + +.case-studies .card-desc-box { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 51, 204, .8); + transition: all .8s ease-in-out; + opacity: 0 +} + +.customer-feedback .customer-cards { + background: #f3f7fb; + font-size: 18px; + margin: 0 12px; + transition: all .7s ease-in-out +} + +.customer-feedback .customer-cards .img-customer { + border-radius: 50%; + transition: all .7s ease-in-out +} + +.customer-feedback .customer-cards:hover { + background: #e4e7ea; + transform: scale(1.02) +} + +.customer-feedback .customer-cards:hover .img-customer { + transform: scale(1.05) +} + +.customer-feedback .customer-cards .content-divider { + width: 73px; + height: 3px +} + +.contact-us .contact-us-bgimage { + background-position: 50%; + background-repeat: no-repeat; + background-size: cover; + text-align: center; + padding: 83px 0 68px +} + +.contact-details .mdi { + font-size: 14px; + color: #6e6e6e; + border-radius: 50px; + border: 1px solid hsla(0, 0%, 59.2%, .24); + padding: 3px 6px; + margin-right: 10px; + transition: all .6s ease-in-out +} + +.contact-details .mdi:hover { + background: #a6a6a6; + color: #fff +} + +.contact-details a { + color: #6e6e6e; + transition: all .6s ease-in-out +} + +.contact-details a:hover, footer a { + color: #a7240e +} + +footer a:hover { + color: #000 +} + +.customer-designation, .section-subtitle { + font-weight: 400 +} + +.header-small .navbar { + background: #fff; + padding: 18px 0; + box-shadow: 0 0 30px 0 rgba(0, 0, 0, .2) +} + +.header-small .navbar.fixed-top { + top: -50px; + margin-top: 50px; + transition: margin-top 1s ease +} + +@media (min-width: 992px) { + .header-small .navbar .btn { + padding: 5px 10px; + margin-left: 20px + } +} + +.owl-nav { + font-size: 30px; + text-align: center; + color: #6e6e6e +} + +.owl-theme .owl-dots .owl-dot { + outline: none +} + +.owl-theme .owl-dots .owl-dot span { + display: inline-block; + width: .9375rem; + height: .9375rem; + box-sizing: border-box; + background-color: #a7240e; + border: 3px solid transparent; + border-radius: 50%; + transform: scale(.6); + transition: all .3s ease-in-out; + opacity: .7 +} + +.owl-theme .owl-dots .owl-dot.active span { + background-color: transparent; + border-color: #a7240e; + transform: scale(.9) +} + +.owl-carousel .owl-item { + padding: 16px 0 +} + +.owl-carousel .owl-item img { + width: auto; + margin: 0 auto +} + +.owl-item:nth-child(odd) .content-divider { + background-color: #000 +} + +.owl-item:nth-child(2n) .content-divider { + background-color: #a7240e +} + +@media (max-width: 575px) { + .container { + padding: 0 30px + } +} + +.navbar { + background-color: #f7f8fa; + padding: .75rem .75rem .75rem .75rem; +} + +.navbar .navbar-toggler { + line-height: inherit +} + +.navbar .navbar-toggler .mdi-menu { + font-size: 30px +} + +.navbar .navbar-toggler .mdi-close { + font-size: 20px +} + +.navbar .navbar-menu-wrapper .navbar-nav .btn-contact-us { + margin-left: 148px; + white-space: nowrap +} + +@media (max-width: 992px) { + .navbar .navbar-menu-wrapper .navbar-nav .btn-contact-us { + margin-left: 0 + } +} + +.navbar .navbar-menu-wrapper .navbar-nav .nav-link { + font-size: .9375rem; + padding: 0 17px; + font-weight: 500; + color: #111 +} + +.navbar .navbar-menu-wrapper .navbar-nav .nav-link:hover { + color: #a7240e; + transition: all .3s ease +} + +.navbar .navbar-menu-wrapper .navbar-nav .nav-link.active { + color: #a7240e +} + +.navbar .navbar-collapse-logo { + display: none +} + +@media (max-width: 992px) { + .navbar .navbar-menu-wrapper .navbar-nav .nav-link { + font-size: 16px + } + + .navbar .navbar-menu-wrapper.navbar-collapse { + position: fixed; + top: 0; + right: 0; + background: #fff; + z-index: 20; + height: 100vh; + padding: 50px 0; + width: 250px; + transform: translateX(100%); + transition: transform .25s ease-in-out + } + + .navbar .navbar-menu-wrapper.navbar-collapse.show { + transform: translateX(0) + } + + .navbar .navbar-menu-wrapper.navbar-collapse ul li { + padding: 10px + } + + .navbar .navbar-collapse-logo { + display: block + } +} + +.grid-margin { + margin-bottom: 100px +} + +@media (max-width: 992px) { + .grid-margin { + margin-bottom: 75px + } +} + +@media (max-width: 756px) { + .grid-margin { + margin-bottom: 60px + } +} + +@media (min-width: 992px) { + .grid-margin-lg-0 { + margin-bottom: 0 + } +} + +.stretch-card { + display: flex; + align-items: stretch; + justify-content: stretch +} + +.stretch-card .card { + width: 100%; + min-width: 100% +} + +.card { + border: none; + border-radius: 6px +} + +.card .card-body { + padding: 43px 36px +} + +.card .card-body .card-title { + font-size: 18px; + margin-bottom: 7px +} + +.form-control { + font-size: 14px; + background-color: rgb(20 20 20); +} + +.form-control, .form-control:focus { + background-color: #f6f7fb; + border: none +} + +.form-control:focus { + box-shadow: none +} + +.modal-content .modal-header { + justify-content: center; + border: none +} + +.modal-content .modal-body { + padding: 0 40px +} + +.modal-content .modal-footer { + border: none; + justify-content: flex-start; + padding: 5px 0 20px 39px +} + +.modal-content .modal-footer { + border: none; + justify-content: flex-start; + padding: 5px 0 20px 39px +} + +.bg-header { + background-color: rgb(221, 191, 134) !important; +} + +.thead-dark { + background-repeat: no-repeat; + background-size: cover; + padding-top: 2vh; + padding-bottom: 2vh; +} + +.thead-blank { + background-color: transparent !important; +} + +textarea, input:not([type="submit"]):not([class="multiselect__input"]):not([class="select2-search__field"]):not([class="vue-treeselect__input"]), select { + background-color: rgb(20 20 20); + color: #fff; + border-radius: .25rem !important; + border: 1px solid #ced4da !important; +} + +.multiselect__tag, .multiselect__option--highlight, .multiselect__option--highlight:after, .vue-treeselect__multi-value-item { + background-color: #cfd5cd !important; + color: #212529 !important; +} + +.multiselect__tag-icon:hover, .multiselect__tag-icon:focus { + background-color: #a7240e !important; +} + +.multiselect__tag-icon:after, .vue-treeselect__icon vue-treeselect__value-remove, .vue-treeselect__value-remove { + color: #212529 !important +} + +.form-control-search { + font-size: 20px; +} + +.ghost { + opacity: 0.5 !important; + background: #b98766 !important; +} \ No newline at end of file diff --git a/cookbook/tables.py b/cookbook/tables.py index 4fa1a7314..6392f791e 100644 --- a/cookbook/tables.py +++ b/cookbook/tables.py @@ -3,8 +3,7 @@ from django.utils.html import format_html from django.utils.translation import gettext as _ from django_tables2.utils import A -from .models import (CookLog, InviteLink, Recipe, RecipeImport, - Storage, Sync, SyncLog, ViewLog) +from .models import CookLog, InviteLink, RecipeImport, Storage, Sync, SyncLog, ViewLog class StorageTable(tables.Table): diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html index 7036ef40a..566027101 100644 --- a/cookbook/templates/base.html +++ b/cookbook/templates/base.html @@ -9,6 +9,7 @@ {% endblock %} + @@ -28,6 +29,7 @@ + @@ -49,7 +51,7 @@ @@ -72,7 +74,7 @@ -
  • + {% plugin_main_nav_templates as plugin_main_nav_templates %} + {% for pn in plugin_main_nav_templates %} + {% include pn %} + {% endfor %}