Merge branch 'develop' into recipe-serving-count

This commit is contained in:
vabene1111
2020-12-28 10:19:52 +01:00
committed by GitHub
59 changed files with 12191 additions and 5803 deletions

View File

@ -37,7 +37,7 @@ class UserPreferenceForm(forms.ModelForm):
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'), '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_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
'plan_share': _('Default user to share newly created meal plan entries with.'), 'plan_share': _('Users with whom newly created meal plan/shopping list entries should be shared by default.'),
'show_recent': _('Show recently viewed recipes on search page.'), 'show_recent': _('Show recently viewed recipes on search page.'),
'ingredient_decimals': _('Number of decimals to round ingredients.'), 'ingredient_decimals': _('Number of decimals to round ingredients.'),
'comments': _('If you want to be able to create and see comments underneath recipes.'), 'comments': _('If you want to be able to create and see comments underneath recipes.'),

View File

@ -0,0 +1,131 @@
import unicodedata
import string
def parse_fraction(x):
if len(x) == 1 and 'fraction' in unicodedata.decomposition(x):
frac_split = unicodedata.decomposition(x[-1:]).split()
return float((frac_split[1]).replace('003', '')) / float((frac_split[3]).replace('003', ''))
else:
frac_split = x.split('/')
if not len(frac_split) == 2:
raise ValueError
try:
return int(frac_split[0]) / int(frac_split[1])
except ZeroDivisionError:
raise ValueError
def parse_amount(x):
amount = 0
unit = ''
did_check_frac = False
end = 0
while end < len(x) and (x[end] in string.digits or ((x[end] == '.' or x[end] == ',') and end + 1 < len(x) and x[end+1] in string.digits)):
end += 1
if end > 0:
amount = float(x[:end].replace(',', '.'))
else:
amount = parse_fraction(x[0])
end += 1
did_check_frac = True
if end < len(x):
if did_check_frac:
unit = x[end:]
else:
try:
amount += parse_fraction(x[end])
unit = x[end+1:]
except ValueError:
unit = x[end:]
return amount, unit
def parse_ingredient_with_comma(tokens):
ingredient = ''
note = ''
start = 0
# search for first occurence of an argument ending in a comma
while start < len(tokens) and not tokens[start].endswith(','):
start += 1
if start == len(tokens):
# no token ending in a comma found -> use everything as ingredient
ingredient = ' '.join(tokens)
else:
ingredient = ' '.join(tokens[:start+1])[:-1]
note = ' '.join(tokens[start+1:])
return ingredient, note
def parse_ingredient(tokens):
ingredient = ''
note = ''
if tokens[-1].endswith(')'):
# last argument ends with closing bracket -> look for opening bracket
start = len(tokens) - 1
while not tokens[start].startswith('(') and not start == 0:
start -= 1
if start == 0:
# the whole list is wrapped in brackets -> assume it is an error (e.g. assumed first argument was the unit)
raise ValueError
elif start < 0:
# no opening bracket anywhere -> just ignore the last bracket
ingredient, note = parse_ingredient_with_comma(tokens)
else:
# opening bracket found -> split in ingredient and note, remove brackets from note
note = ' '.join(tokens[start:])[1:-1]
ingredient = ' '.join(tokens[:start])
else:
ingredient, note = parse_ingredient_with_comma(tokens)
return ingredient, note
def parse(x):
# initialize default values
amount = 0
unit = ''
ingredient = ''
note = ''
tokens = x.split()
if len(tokens) == 1:
# there only is one argument, that must be the ingredient
ingredient = tokens[0]
else:
try:
# try to parse first argument as amount
amount, unit = parse_amount(tokens[0])
# only try to parse second argument as amount if there are at least three arguments
# if it already has a unit there can't be a fraction for the amount
if len(tokens) > 2:
try:
if not unit == '':
# a unit is already found, no need to try the second argument for a fraction
# probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except
raise ValueError
# try to parse second argument as amount and add that, in case of '2 1/2' or '2 ½'
amount += parse_fraction(tokens[1])
# assume that units can't end with a comma
if len(tokens) > 3 and not tokens[2].endswith(','):
# try to use third argument as unit and everything else as ingredient, use everything as ingredient if it fails
try:
ingredient, note = parse_ingredient(tokens[3:])
unit = tokens[2]
except ValueError:
ingredient, note = parse_ingredient(tokens[2:])
else:
ingredient, note = parse_ingredient(tokens[2:])
except ValueError:
# assume that units can't end with a comma
if not tokens[1].endswith(','):
# try to use second argument as unit and everything else as ingredient, use everything as ingredient if it fails
try:
ingredient, note = parse_ingredient(tokens[2:])
unit = tokens[1]
except ValueError:
ingredient, note = parse_ingredient(tokens[1:])
else:
ingredient, note = parse_ingredient(tokens[1:])
else:
# only two arguments, first one is the amount which means this is the ingredient
ingredient = tokens[1]
except ValueError:
# can't parse first argument as amount -> no unit -> parse everything as ingredient
ingredient, note = parse_ingredient(tokens)
return amount, unit.strip(), ingredient.strip(), note.strip()

View File

@ -141,7 +141,7 @@ class OwnerRequiredMixin(object):
return HttpResponseRedirect(reverse_lazy('login')) return HttpResponseRedirect(reverse_lazy('login'))
else: else:
if not is_object_owner(request.user, self.get_object()): if not is_object_owner(request.user, self.get_object()):
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as its not owned by you!')) messages.add_message(request, messages.ERROR, _('You cannot interact with this object as it is not owned by you!'))
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs) return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
@ -155,7 +155,7 @@ class CustomIsOwner(permissions.BasePermission):
verifies user has ownership over object verifies user has ownership over object
(either user or created_by or user is request user) (either user or created_by or user is request user)
""" """
message = _('You cannot interact with this object as its not owned by you!') message = _('You cannot interact with this object as it is not owned by you!')
def has_permission(self, request, view): def has_permission(self, request, view):
return request.user.is_authenticated return request.user.is_authenticated
@ -169,7 +169,7 @@ class CustomIsShared(permissions.BasePermission): # TODO function duplicate/too
Custom permission class for django rest framework views Custom permission class for django rest framework views
verifies user is shared for the object he is trying to access verifies user is shared for the object he is trying to access
""" """
message = _('You cannot interact with this object as its not owned by you!') message = _('You cannot interact with this object as it is not owned by you!')
def has_permission(self, request, view): def has_permission(self, request, view):
return request.user.is_authenticated return request.user.is_authenticated

View File

@ -11,6 +11,7 @@ from django.utils.dateparse import parse_duration
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from cookbook.models import Keyword from cookbook.models import Keyword
from cookbook.helper.ingredient_parser import parse as parse_ingredient
def get_from_html(html_text, url): def get_from_html(html_text, url):
@ -70,39 +71,12 @@ def find_recipe_json(ld_json, url):
ingredients = [] ingredients = []
for x in ld_json['recipeIngredient']: for x in ld_json['recipeIngredient']:
ingredient_split = x.split() try:
ingredient = None amount, unit, ingredient, note = parse_ingredient(x)
amount = 0 if ingredient:
unit = '' ingredients.append({'amount': amount, 'unit': {'text': unit, 'id': random.randrange(10000, 99999)}, 'ingredient': {'text': ingredient, 'id': random.randrange(10000, 99999)}, "note": note, 'original': x})
if len(ingredient_split) > 2: except:
ingredient = " ".join(ingredient_split[2:]) pass
unit = ingredient_split[1]
try:
if 'fraction' in unicodedata.decomposition(ingredient_split[0]):
frac_split = unicodedata.decomposition(ingredient_split[0]).split()
amount = round(float((frac_split[1]).replace('003', '')) / float((frac_split[3]).replace('003', '')), 3)
else:
raise TypeError
except TypeError: # raised by unicodedata.decomposition if there was no unicode character in parsed data
try:
amount = float(ingredient_split[0].replace(',', '.'))
except ValueError:
amount = 0
ingredient = " ".join(ingredient_split)
if len(ingredient_split) == 2:
ingredient = " ".join(ingredient_split[1:])
unit = ''
try:
amount = float(ingredient_split[0].replace(',', '.'))
except ValueError:
amount = 0
ingredient = " ".join(ingredient_split)
if len(ingredient_split) == 1:
ingredient = " ".join(ingredient_split)
if ingredient:
ingredients.append({'amount': amount, 'unit': {'text': unit, 'id': random.randrange(10000, 99999)}, 'ingredient': {'text': ingredient, 'id': random.randrange(10000, 99999)}, 'original': x})
ld_json['recipeIngredient'] = ingredients ld_json['recipeIngredient'] = ingredients
else: else:
@ -158,7 +132,7 @@ def find_recipe_json(ld_json, url):
else: else:
ld_json['recipeInstructions'] = '' ld_json['recipeInstructions'] = ''
ld_json['recipeInstructions'] += '\n\n' + _('Imported from ') + url ld_json['recipeInstructions'] += '\n\n' + _('Imported from') + ' ' + url
if 'image' in ld_json: if 'image' in ld_json:
# check if list of images is returned, take first if so # check if list of images is returned, take first if so

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
# Generated by Django 3.1.4 on 2020-12-26 14:51
from django.db import migrations
def migrate_empty_units(apps, schema_editor):
Unit = apps.get_model('cookbook', 'Unit')
Ingredient = apps.get_model('cookbook', 'Ingredient')
empty_units = Unit.objects.filter(name='').all()
for x in empty_units:
for i in Ingredient.objects.all():
if i.unit == x:
i.unit = None
i.save()
x.delete()
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0090_auto_20201214_1359'),
]
operations = [
migrations.RunPython(migrate_empty_units),
]

View File

@ -5,6 +5,7 @@ from datetime import date, timedelta
from annoying.fields import AutoOneToOneField from annoying.fields import AutoOneToOneField
from django.contrib import auth from django.contrib import auth
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.core.validators import MinLengthValidator
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.db import models from django.db import models
from django_random_queryset import RandomManager from django_random_queryset import RandomManager
@ -136,7 +137,7 @@ class Keyword(models.Model):
class Unit(models.Model): class Unit(models.Model):
name = models.CharField(unique=True, max_length=128) name = models.CharField(unique=True, max_length=128, validators=[MinLengthValidator(1)])
description = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True)
def __str__(self): def __str__(self):

View File

@ -84,7 +84,7 @@
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'list_keyword,data_batch_edit' %}active{% endif %}"> <li class="nav-item dropdown {% if request.resolver_match.url_name in 'list_keyword,data_batch_edit' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"> aria-haspopup="true" aria-expanded="false">
<i class="fas fa-tags"></i> {% trans 'Tags' %} <i class="fas fa-tags"></i> {% trans 'Keywords' %}
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="{% url 'list_keyword' %}"><i <a class="dropdown-item" href="{% url 'list_keyword' %}"><i
@ -139,7 +139,7 @@
{% endif %} {% endif %}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'docs_markdown' %}"><i <a class="dropdown-item" href="{% url 'docs_markdown' %}"><i
class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Help' %}</a> class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Guide' %}</a>
<a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i <a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i
class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a> class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a>
<a class="dropdown-item" href="{% url 'docs_api' %}"><i <a class="dropdown-item" href="{% url 'docs_api' %}"><i

View File

@ -11,9 +11,9 @@
<div class="card border-info"> <div class="card border-info">
<div class="card-body text-info"> <div class="card-body text-info">
<p class="card-text"> <p class="card-text">
{% trans 'On this Page you can manage all storage folder locations that should be monitored and synced' %} {% trans 'On this Page you can manage all storage folder locations that should be monitored and synced.' %}
<br/> <br/>
{% trans 'The path must be in the following format' %} <code>/Folder/RecipesFolder</code> {% trans 'The path must be in the following format' %}: <code>/Folder/RecipesFolder</code>
</p> </p>
<form method="POST" class="post-form">{% csrf_token %} <form method="POST" class="post-form">{% csrf_token %}
{{ form|crispy }} {{ form|crispy }}

View File

@ -23,7 +23,7 @@
<h4>{% trans 'Units' %}</h4> <h4>{% trans 'Units' %}</h4>
<form action="{% url 'edit_food' %}" method="post" <form action="{% url 'edit_food' %}" method="post"
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two units ?' %}')"> onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two units?' %}')">
{% csrf_token %} {% csrf_token %}
{{ units_form|crispy }} {{ units_form|crispy }}
<button class="btn btn-danger" type="submit" <button class="btn btn-danger" type="submit"
@ -33,7 +33,7 @@
<h4>{% trans 'Ingredients' %}</h4> <h4>{% trans 'Ingredients' %}</h4>
<form action="{% url 'edit_food' %}" method="post" <form action="{% url 'edit_food' %}" method="post"
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two ingredients ?' %}')"> onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two ingredients?' %}')">
{% csrf_token %} {% csrf_token %}
{{ food_form|crispy }} {{ food_form|crispy }}
<button class="btn btn-danger" type="submit"> <button class="btn btn-danger" type="submit">

View File

@ -62,7 +62,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="collapse col-md-12" id="collapse_adv_search"> <div class="collapse col-md-12{% if filter.data.keywords or filter.data.foods or filter.data.internal and not filter.data.internal == "unknown" %} show{% endif %}" id="collapse_adv_search">
<div style="margin-top: 1vh"> <div style="margin-top: 1vh">
{{ filter.form.keywords | as_crispy_field }} {{ filter.form.keywords | as_crispy_field }}
</div> </div>

View File

@ -194,9 +194,9 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="id_all_keywords">{% trans 'All Keywords' %}</label><br/> {% trans 'All Keywords' %}<br/>
<input id="id_all_keywords" type="checkbox" <input id="id_all_keywords" type="checkbox"
v-model="all_keywords"> {% trans 'Import all Keywords not only the ones already existing.' %} v-model="all_keywords"> <label for="id_all_keywords">{% trans 'Import all Keywords not only the ones already existing.' %}</label>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -0,0 +1,26 @@
import json
from django.contrib import auth
from django.db.models import ProtectedError
from django.urls import reverse
from cookbook.models import Storage, Sync, Keyword, ShoppingList, Recipe
from cookbook.tests.views.test_views import TestViews
class TestApiShopping(TestViews):
def setUp(self):
super(TestApiShopping, self).setUp()
self.internal_recipe = Recipe.objects.create(
name='Test',
internal=True,
created_by=auth.get_user(self.user_client_1)
)
def test_shopping_view_permissions(self):
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 200), (self.user_client_1, 200),
(self.user_client_2, 200), (self.admin_client_1, 200), (self.superuser_client, 200)],
reverse('api:recipe-detail', args={self.internal_recipe.id}))
# TODO add tests for editing

View File

@ -1,5 +1,6 @@
import json import json
from cookbook.helper.ingredient_parser import parse_ingredient
from cookbook.helper.recipe_url_import import get_from_html from cookbook.helper.recipe_url_import import get_from_html
from cookbook.tests.test_setup import TestBase from cookbook.tests.test_setup import TestBase
@ -8,16 +9,16 @@ class TestEditsRecipe(TestBase):
def test_ld_json(self): def test_ld_json(self):
test_list = [ test_list = [
{'file': 'cookbook/tests/resources/websites/ld_json_1.html', 'result_length': 3128}, {'file': 'cookbook/tests/resources/websites/ld_json_1.html', 'result_length': 3218},
{'file': 'cookbook/tests/resources/websites/ld_json_2.html', 'result_length': 1450}, {'file': 'cookbook/tests/resources/websites/ld_json_2.html', 'result_length': 1510},
{'file': 'cookbook/tests/resources/websites/ld_json_3.html', 'result_length': 1545}, {'file': 'cookbook/tests/resources/websites/ld_json_3.html', 'result_length': 1629},
{'file': 'cookbook/tests/resources/websites/ld_json_4.html', 'result_length': 1657}, {'file': 'cookbook/tests/resources/websites/ld_json_4.html', 'result_length': 1729},
{'file': 'cookbook/tests/resources/websites/ld_json_itemList.html', 'result_length': 3131}, {'file': 'cookbook/tests/resources/websites/ld_json_itemList.html', 'result_length': 3200},
{'file': 'cookbook/tests/resources/websites/ld_json_multiple.html', 'result_length': 1546}, {'file': 'cookbook/tests/resources/websites/ld_json_multiple.html', 'result_length': 1606},
{'file': 'cookbook/tests/resources/websites/micro_data_1.html', 'result_length': 1022}, {'file': 'cookbook/tests/resources/websites/micro_data_1.html', 'result_length': 1079},
{'file': 'cookbook/tests/resources/websites/micro_data_2.html', 'result_length': 1384}, {'file': 'cookbook/tests/resources/websites/micro_data_2.html', 'result_length': 1429},
{'file': 'cookbook/tests/resources/websites/micro_data_3.html', 'result_length': 1100}, {'file': 'cookbook/tests/resources/websites/micro_data_3.html', 'result_length': 1148},
{'file': 'cookbook/tests/resources/websites/micro_data_4.html', 'result_length': 4231}, {'file': 'cookbook/tests/resources/websites/micro_data_4.html', 'result_length': 4396},
] ]
for test in test_list: for test in test_list:
@ -26,3 +27,61 @@ class TestEditsRecipe(TestBase):
parsed_content = json.loads(get_from_html(file.read(), 'test_url').content) parsed_content = json.loads(get_from_html(file.read(), 'test_url').content)
self.assertEqual(len(str(parsed_content)), test['result_length']) self.assertEqual(len(str(parsed_content)), test['result_length'])
file.close() file.close()
def test_ingredient_parser(self):
expectations = {
"2¼ l Wasser": (2.25, "l", "Wasser", ""),
"2¼l Wasser": (2.25, "l", "Wasser", ""),
"3l Wasser": (3, "l", "Wasser", ""),
"4 l Wasser": (4, "l", "Wasser", ""),
"½l Wasser": (0.5, "l", "Wasser", ""),
"⅛ Liter Sauerrahm": (0.125, "Liter", "Sauerrahm", ""),
"5 Zwiebeln": (5, "", "Zwiebeln", ""),
"3 Zwiebeln, gehackt": (3, "", "Zwiebeln", "gehackt"),
"5 Zwiebeln (gehackt)": (5, "", "Zwiebeln", "gehackt"),
"1 Zwiebel(n)": (1, "", "Zwiebel(n)", ""),
"4 1/2 Zwiebeln": (4.5, "", "Zwiebeln", ""),
"4 ½ Zwiebeln": (4.5, "", "Zwiebeln", ""),
"etwas Mehl": (0, "", "etwas Mehl", ""),
"Öl zum Anbraten": (0, "", "Öl zum Anbraten", ""),
"n. B. Knoblauch, zerdrückt": (0, "", "n. B. Knoblauch", "zerdrückt"),
"Kräuter, mediterrane (Oregano, Rosmarin, Basilikum)": (
0, "", "Kräuter, mediterrane", "Oregano, Rosmarin, Basilikum"),
"600 g Kürbisfleisch (Hokkaido), geschält, entkernt und geraspelt": (
600, "g", "Kürbisfleisch (Hokkaido)", "geschält, entkernt und geraspelt"),
"Muskat": (0, "", "Muskat", ""),
"200 g Mehl, glattes": (200, "g", "Mehl", "glattes"),
"1 Ei(er)": (1, "", "Ei(er)", ""),
"1 Prise(n) Salz": (1, "Prise(n)", "Salz", ""),
"etwas Wasser, lauwarmes": (0, "", "etwas Wasser", "lauwarmes"),
"Strudelblätter, fertige, für zwei Strudel": (0, "", "Strudelblätter", "fertige, für zwei Strudel"),
"barrel-aged Bourbon": (0, "", "barrel-aged Bourbon", ""),
"golden syrup": (0, "", "golden syrup", ""),
"unsalted butter, for greasing": (0, "", "unsalted butter", "for greasing"),
"unsalted butter , for greasing": (0, "", "unsalted butter", "for greasing"), # trim
"1 small sprig of fresh rosemary": (1, "small", "sprig of fresh rosemary", ""),
# does not always work perfectly!
"75 g fresh breadcrumbs": (75, "g", "fresh breadcrumbs", ""),
"4 acorn squash , or onion squash (600-800g)": (4, "acorn", "squash , or onion squash", "600-800g"),
"1 x 250 g packet of cooked mixed grains , such as spelt and wild rice": (
1, "x", "250 g packet of cooked mixed grains", "such as spelt and wild rice"),
"1 big bunch of fresh mint , (60g)": (1, "big", "bunch of fresh mint ,", "60g"),
"1 large red onion": (1, "large", "red onion", ""),
# "2-3 TL Curry": (), # idk what it should use here either
"1 Zwiebel gehackt": (1, "Zwiebel", "gehackt", ""),
"1 EL Kokosöl": (1, "EL", "Kokosöl", ""),
"0.5 paket jäst (à 50 g)": (0.5, "paket", "jäst", "à 50 g"),
"ägg": (0, "", "ägg", ""),
"50 g smör eller margarin": (50, "g", "smör eller margarin", ""),
"3,5 l Wasser": (3.5, "l", "Wasser", ""),
"3.5 l Wasser": (3.5, "l", "Wasser", "")
}
# for German you could say that if an ingredient does not have an amount and it starts with a lowercase letter, then that is a unit ("etwas", "evtl.")
# does not apply to English tho
errors = 0
count = 0
for key, val in expectations.items():
count += 1
parsed = parse_ingredient(key)
self.assertNotEqual(val, parsed)

View File

@ -125,7 +125,7 @@ def import_url(request):
ingredient = Ingredient() ingredient = Ingredient()
ingredient.food, f_created = Food.objects.get_or_create(name=ing['ingredient']['text']) ingredient.food, f_created = Food.objects.get_or_create(name=ing['ingredient']['text'])
if ing['unit']: if ing['unit'] and ing['unit']['text'] != '':
ingredient.unit, u_created = Unit.objects.get_or_create(name=ing['unit']['text']) ingredient.unit, u_created = Unit.objects.get_or_create(name=ing['unit']['text'])
# TODO properly handle no_amount recipes # TODO properly handle no_amount recipes
@ -143,7 +143,7 @@ def import_url(request):
step.ingredients.add(ingredient) step.ingredients.add(ingredient)
print(ingredient) print(ingredient)
if data['image'] != '': if 'image' in data and data['image'] != '':
try: try:
response = requests.get(data['image']) response = requests.get(data['image'])
img = Image.open(BytesIO(response.content)) img = Image.open(BytesIO(response.content))

View File

@ -129,7 +129,7 @@ class RecipeBookEntryDelete(GroupRequiredMixin, DeleteView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
if not (obj.book.created_by == request.user or request.user.is_superuser): if not (obj.book.created_by == request.user or request.user.is_superuser):
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as its not owned by you!')) messages.add_message(request, messages.ERROR, _('You cannot interact with this object as it is not owned by you!'))
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
return super(RecipeBookEntryDelete, self).dispatch(request, *args, **kwargs) return super(RecipeBookEntryDelete, self).dispatch(request, *args, **kwargs)

View File

@ -239,27 +239,33 @@ def edit_ingredients(request):
if units_form.is_valid(): if units_form.is_valid():
new_unit = units_form.cleaned_data['new_unit'] new_unit = units_form.cleaned_data['new_unit']
old_unit = units_form.cleaned_data['old_unit'] old_unit = units_form.cleaned_data['old_unit']
recipe_ingredients = Ingredient.objects.filter(unit=old_unit).all() if new_unit != old_unit:
for i in recipe_ingredients: recipe_ingredients = Ingredient.objects.filter(unit=old_unit).all()
i.unit = new_unit for i in recipe_ingredients:
i.save() i.unit = new_unit
i.save()
old_unit.delete() old_unit.delete()
success = True success = True
messages.add_message(request, messages.SUCCESS, _('Units merged!')) messages.add_message(request, messages.SUCCESS, _('Units merged!'))
else:
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix) food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix)
if food_form.is_valid(): if food_form.is_valid():
new_food = food_form.cleaned_data['new_food'] new_food = food_form.cleaned_data['new_food']
old_food = food_form.cleaned_data['old_food'] old_food = food_form.cleaned_data['old_food']
ingredients = Ingredient.objects.filter(food=old_food).all() if new_food != old_food:
for i in ingredients: ingredients = Ingredient.objects.filter(food=old_food).all()
i.food = new_food for i in ingredients:
i.save() i.food = new_food
i.save()
old_food.delete() old_food.delete()
success = True success = True
messages.add_message(request, messages.SUCCESS, _('Foods merged!')) messages.add_message(request, messages.SUCCESS, _('Foods merged!'))
else:
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
if success: if success:
units_form = UnitMergeForm() units_form = UnitMergeForm()

View File

@ -1,2 +1,2 @@
CALL venv\Scripts\activate.bat CALL venv\Scripts\activate.bat
python manage.py makemessages -i venv -l de -l nl -l rn -l fr -l tr -l pt -l en python manage.py makemessages -i venv -l de -l nl -l rn -l fr -l tr -l pt -l en -l it -l ca -l es -l zh_CN -l hu_HU

View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
msgid "English"
msgstr ""
#: recipes/settings.py:176
msgid "German"
msgstr ""
#: recipes/settings.py:177
msgid "Dutch"
msgstr ""
#: recipes/settings.py:178
msgid "French"
msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
msgid "English"
msgstr ""
#: recipes/settings.py:176
msgid "German"
msgstr ""
#: recipes/settings.py:177
msgid "Dutch"
msgstr ""
#: recipes/settings.py:178
msgid "French"
msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: recipes/settings.py:175
msgid "English"
msgstr ""
#: recipes/settings.py:176
msgid "German"
msgstr ""
#: recipes/settings.py:177
msgid "Dutch"
msgstr ""
#: recipes/settings.py:178
msgid "French"
msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:175
msgid "English"
msgstr ""
#: recipes/settings.py:176
msgid "German"
msgstr ""
#: recipes/settings.py:177
msgid "Dutch"
msgstr ""
#: recipes/settings.py:178
msgid "French"
msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,18 +17,30 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-15 21:39+0100\n" "POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,30 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: .\recipes\settings.py:175 #: recipes/settings.py:175
msgid "English" msgid "English"
msgstr "" msgstr ""
#: .\recipes\settings.py:176 #: recipes/settings.py:176
msgid "German" msgid "German"
msgstr "" msgstr ""
#: .\recipes\settings.py:177 #: recipes/settings.py:177
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: .\recipes\settings.py:178 #: recipes/settings.py:178
msgid "French" msgid "French"
msgstr "" msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -0,0 +1,46 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: recipes/settings.py:175
msgid "English"
msgstr ""
#: recipes/settings.py:176
msgid "German"
msgstr ""
#: recipes/settings.py:177
msgid "Dutch"
msgstr ""
#: recipes/settings.py:178
msgid "French"
msgstr ""
#: recipes/settings.py:179
msgid "Catalan"
msgstr ""
#: recipes/settings.py:180
msgid "Spanish"
msgstr ""
#: recipes/settings.py:181
msgid "Italian"
msgstr ""

View File

@ -177,6 +177,8 @@ LANGUAGES = [
('nl', _('Dutch')), ('nl', _('Dutch')),
('fr', _('French')), ('fr', _('French')),
('ca', _('Catalan')), ('ca', _('Catalan')),
('es', _('Spanish')),
('it', _('Italian')),
] ]
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)