13 changed files with 773 additions and 0 deletions
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
|
||||
SLUG_HELP = _("Kurzfassung des Namens für die Adresszeile im Browser. Vorzugsweise englisch, keine Umlaute, nur Bindestrich als Sonderzeichen.") |
||||
|
@ -0,0 +1,224 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2016 |
||||
""" |
||||
Extends django.utils.dateformat |
||||
""" |
||||
|
||||
|
||||
import datetime |
||||
import re |
||||
from django.conf import settings |
||||
from django.utils.dateformat import DateFormat, re_escaped |
||||
from django.utils.formats import get_format |
||||
from django.utils.encoding import force_text |
||||
from django.utils.translation import get_language, ugettext_lazy as _ |
||||
|
||||
|
||||
# Adding "q" |
||||
re_formatchars = re.compile(r'(?<!\\)([aAbBcdDeEfFgGhHiIjlLmMnNoOPqrsStTUuwWyYzZ])') |
||||
|
||||
|
||||
class ExtendedFormat(DateFormat): |
||||
def q(self): |
||||
""" |
||||
Time, in 24-hour hours and minutes, with minutes left off if they're |
||||
zero. |
||||
Examples: '1', '1:30', '13:05', '14' |
||||
Proprietary extension. |
||||
""" |
||||
if self.data.minute == 0: |
||||
return self.G() |
||||
return '%s:%s' % (self.G(), self.i()) |
||||
|
||||
def format(self, formatstr): |
||||
pieces = [] |
||||
for i, piece in enumerate(re_formatchars.split(force_text(formatstr))): |
||||
if i % 2: |
||||
pieces.append(force_text(getattr(self, piece)())) |
||||
elif piece: |
||||
pieces.append(re_escaped.sub(r'\1', piece)) |
||||
return ''.join(pieces) |
||||
|
||||
|
||||
def format(value, format): |
||||
# Copy of django.utils.dateformat.format, using our extended formatter |
||||
df = ExtendedFormat(value) |
||||
return df.format(format) |
||||
|
||||
|
||||
def time_format(value, format=None, use_l10n=None): |
||||
# Copy of django.utils.dateformat.time_format, using our extended formatter |
||||
tf = ExtendedFormat(value) |
||||
return tf.format(get_format(format or 'DATE_FORMAT', use_l10n=use_l10n)) |
||||
|
||||
|
||||
def date_format(value, format=None, use_l10n=None): |
||||
df = ExtendedFormat(value) |
||||
return df.format(get_format(format or 'DATE_FORMAT', use_l10n=use_l10n)) |
||||
|
||||
|
||||
def _normalize_variant(variant): |
||||
if variant.lower() not in ('short', 'long', ''): |
||||
variant = 'short' |
||||
if variant and not variant.endswith("_"): |
||||
variant = variant + "_" |
||||
return variant.upper() |
||||
|
||||
|
||||
def format_date_range(from_date, to_date, variant='short'): |
||||
""" |
||||
>>> import datetime |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,1,20)) |
||||
'15. - 20.01.2009.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,2,20)) |
||||
'15.01. - 20.02.2009.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,2,20)) |
||||
'15.01.2009. - 20.02.2010.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,1,20)) |
||||
'15.01.2009. - 20.01.2010.' |
||||
""" |
||||
if not (from_date or to_date): |
||||
return "" |
||||
|
||||
variant = _normalize_variant(variant) |
||||
|
||||
# Only deal with dates, ignoring time |
||||
def datetime_to_date(dt): |
||||
try: |
||||
return dt.date() |
||||
except AttributeError: |
||||
return dt |
||||
from_date = datetime_to_date(from_date) |
||||
to_date = datetime_to_date(to_date) |
||||
|
||||
from_format = to_format = get_format(variant + 'DATE_FORMAT') |
||||
|
||||
if from_date == to_date or not to_date: |
||||
return date_format(from_date, get_format(from_format)) |
||||
else: |
||||
if (from_date.year == to_date.year): |
||||
from_format = get_format(variant + 'DAYMONTH_FORMAT') or 'd/m/' |
||||
if (from_date.month == to_date.month): |
||||
from_format = get_format(variant + 'DAYONLY_FORMAT') or 'd' |
||||
|
||||
f = t = "" |
||||
if from_date: |
||||
f = date_format(from_date, get_format(from_format)) |
||||
if to_date: |
||||
t = date_format(to_date, get_format(to_format)) |
||||
|
||||
separator = get_format('DATE_RANGE_SEPARATOR') or " - " |
||||
return separator.join((f, t)) |
||||
|
||||
|
||||
def format_time_range(from_time, to_time, variant='short'): |
||||
""" |
||||
Knows how to deal with left out from_time/to_time values. |
||||
""" |
||||
if not (from_time or to_time): |
||||
return "" |
||||
|
||||
variant = _normalize_variant(variant) |
||||
|
||||
from_format = to_format = "q" # get_format(variant + 'TIME_FORMAT') |
||||
|
||||
if from_time == to_time or not to_time: |
||||
return time_format(from_time, get_format(from_format)) |
||||
else: |
||||
f = t = "" |
||||
if from_time: |
||||
f = time_format(from_time, get_format(from_format)) |
||||
if to_time: |
||||
t = time_format(to_time, get_format(to_format)) |
||||
|
||||
separator = get_format('DATE_RANGE_SEPARATOR') or "–" |
||||
return separator.join((f, t)) |
||||
|
||||
|
||||
def format_timespan_range(timespan_object, force_wholeday=False, variant='short'): |
||||
""" |
||||
For Timespan-objects, i.e. object with start_date, end_date, start_time and end_time properties. |
||||
|
||||
Multiday or force_wholeday: |
||||
"10.07.2016-11.07.2016" |
||||
|
||||
Single days: |
||||
"10.07.2016 11 Uhr" |
||||
"10.07.2016 11-14 Uhr" |
||||
|
||||
>>> import datetime |
||||
>>> sd, ed = datetime.date(2009,1,15), datetime.date(2009,1,20) |
||||
>>> st, et = datetime.date(2009,1,15), datetime.date(2009,1,20) |
||||
>>> class TestObject(object): |
||||
>>> start_date = None |
||||
>>> end_date = None |
||||
>>> start_time = None |
||||
>>> end_time = None |
||||
>>> obj = TestObject() |
||||
>>> obj.start_date = obj.end_date = sd |
||||
>>> format_timespan_range(obj) |
||||
'15.01.–20.01.2009' |
||||
|
||||
""" |
||||
variant = _normalize_variant(variant) |
||||
|
||||
rv = format_date_range(timespan_object.start_date, timespan_object.end_date, variant) |
||||
|
||||
if (timespan_object.is_multiday() or |
||||
not timespan_object.start_time or |
||||
force_wholeday): |
||||
# Don't show timespan |
||||
return rv |
||||
else: |
||||
rv = _("%(daterange)s %(timespan)s Uhr") % { |
||||
'daterange': rv, |
||||
'timespan': format_time_range(timespan_object.start_time, timespan_object.end_time, variant) |
||||
} |
||||
return rv |
||||
|
||||
|
||||
def format_partial_date(year=None, month=None, day=None, variant='short'): |
||||
""" |
||||
>>> format_partial_date(2008) |
||||
2008 |
||||
>>> format_partial_date(2008, 3) |
||||
2008 |
||||
>>> format_partial_date(2008) |
||||
2008 |
||||
>>> format_partial_date(2008) |
||||
2008 |
||||
>>> format_partial_date(2008) |
||||
2008 |
||||
>>> format_partial_date(2008) |
||||
2008 |
||||
""" |
||||
if year and month and day: |
||||
format_name = 'DATE_FORMAT' |
||||
elif year and month: |
||||
format_name = 'YEARMONTH_FORMAT' |
||||
elif month and day: |
||||
format_name = 'DAYMONTH_FORMAT' |
||||
elif year: |
||||
format_name = 'YEAR_FORMAT' |
||||
elif month: |
||||
format_name = 'MONTH_FORMAT' |
||||
elif day: |
||||
format_name = 'DAYONLY_FORMAT' |
||||
|
||||
name = _normalize_variant(variant) + format_name |
||||
# TODO Django bug or what? Sometimes get_language returns None, therefore force a language here |
||||
partial_date_format = get_format(name, lang=get_language() or settings.LANGUAGE_CODE) |
||||
return date_format(datetime.date(year or 2000, month or 1, day or 1), partial_date_format) |
||||
|
||||
|
||||
# TODO Add format_partial_date_range function |
||||
|
||||
|
||||
def _test(): |
||||
import doctest |
||||
doctest.testmod() |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
_test() |
@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2008-2015 |
||||
|
||||
import re |
||||
from django.db.models import fields |
||||
from django.template.defaultfilters import slugify |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
from . import SLUG_HELP |
||||
|
||||
|
||||
DEFAULT_SLUG = _("item") |
||||
|
||||
|
||||
def unique_slug(instance, slug_field, slug_value, max_length=50, queryset=None): |
||||
""" |
||||
TODO Doesn't work with model inheritance, where the slug field is part of the parent class. |
||||
""" |
||||
if not slug_value: |
||||
raise ValueError("Cannot uniquify empty slug") |
||||
orig_slug = slug = slugify(slug_value) |
||||
index = 0 |
||||
if not queryset: |
||||
queryset = instance.__class__._default_manager.get_queryset() |
||||
|
||||
def get_similar_slugs(slug): |
||||
return queryset.exclude(pk=instance.pk) \ |
||||
.filter(**{"%s__istartswith" % slug_field: slug}).values_list(slug_field, flat=True) |
||||
|
||||
similar_slugs = get_similar_slugs(slug) |
||||
while slug in similar_slugs or len(slug) > max_length: |
||||
index += 1 |
||||
slug = "%s-%i" % (orig_slug, index) |
||||
if len(slug) > max_length: |
||||
orig_slug = orig_slug[:-(len(slug) - max_length)] |
||||
slug = "%s-%i" % (orig_slug, index) |
||||
similar_slugs = get_similar_slugs(orig_slug) |
||||
return slug |
||||
|
||||
|
||||
def unique_slug2(instance, slug_source, slug_field): |
||||
slug = slugify(slug_source) |
||||
all_slugs = [sl.values()[0] for sl in instance.__class__._default_manager.values(slug_field)] |
||||
if slug in all_slugs: |
||||
counter_finder = re.compile(r'-\d+$') |
||||
counter = 2 |
||||
slug = "%s-%i" % (slug, counter) |
||||
while slug in all_slugs: |
||||
slug = re.sub(counter_finder, "-%i" % counter, slug) |
||||
counter += 1 |
||||
return slug |
||||
|
||||
|
||||
class AutoSlugField(fields.SlugField): |
||||
# AutoSlugField based on http://www.djangosnippets.org/snippets/728/ |
||||
|
||||
def __init__(self, *args, **kwargs): |
||||
kwargs.setdefault('max_length', 50) |
||||
kwargs.setdefault('help_text', SLUG_HELP) |
||||
if 'populate_from' in kwargs: |
||||
self.populate_from = kwargs.pop('populate_from') |
||||
self.unique_slug = kwargs.pop('unique_slug', False) |
||||
super(AutoSlugField, self).__init__(*args, **kwargs) |
||||
|
||||
def pre_save(self, model_instance, add): |
||||
value = getattr(model_instance, self.attname) |
||||
if not value: |
||||
if hasattr(self, 'populate_from'): |
||||
# Follow dotted path (e.g. "occupation.corporation.name") |
||||
value = reduce(lambda obj, attr: getattr(obj, attr), self.populate_from.split("."), model_instance) |
||||
if callable(value): |
||||
value = value() |
||||
if not value: |
||||
value = DEFAULT_SLUG |
||||
if self.unique_slug: |
||||
return unique_slug(model_instance, self.name, value, max_length=self.max_length) |
||||
else: |
||||
return super(AutoSlugField, self).pre_save(model_instance, add) |
@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2016 |
||||
|
||||
from django import forms |
||||
from django.contrib import admin |
||||
from django.utils.translation import ugettext_lazy as _ |
||||
|
||||
|
||||
# from http://stackoverflow.com/questions/877723/inline-form-validation-in-django#877920 |
||||
|
||||
class MandatoryInlineFormSet(forms.models.BaseInlineFormSet): |
||||
""" |
||||
Make sure at least one inline form is valid. |
||||
""" |
||||
mandatory_error_message = _("Bitte mindestens %(min_num)s %(name)s angeben.") |
||||
|
||||
def is_valid(self): |
||||
return super(MandatoryInlineFormSet, self).is_valid() and \ |
||||
not any([bool(e) for e in self.errors]) |
||||
|
||||
def clean(self): |
||||
# get forms that actually have valid data |
||||
count = 0 |
||||
for form in self.forms: |
||||
try: |
||||
if form.cleaned_data and not form.cleaned_data.get('DELETE', False): |
||||
count += 1 |
||||
except AttributeError: |
||||
# annoyingly, if a subform is invalid Django explicity raises |
||||
# an AttributeError for cleaned_data |
||||
pass |
||||
if count < self.min_num: |
||||
if self.min_num > 1: |
||||
name = self.model._meta.verbose_name_plural |
||||
else: |
||||
name = self.model._meta.verbose_name |
||||
raise forms.ValidationError( |
||||
self.mandatory_error_message % { |
||||
'min_num': self.min_num, |
||||
'name': name, |
||||
} |
||||
) |
||||
|
||||
|
||||
class MandatoryTabularInline(admin.TabularInline): |
||||
formset = MandatoryInlineFormSet |
||||
|
||||
|
||||
class MandatoryStackedInline(admin.StackedInline): |
||||
formset = MandatoryInlineFormSet |
@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2016 |
||||
# improved from https://djangosnippets.org/snippets/1405/ |
||||
|
||||
|
||||
from django import template |
||||
from ..dateformat import date_format, get_format |
||||
|
||||
|
||||
""" |
||||
# TODO Describe custom formats |
||||
""" |
||||
|
||||
|
||||
register = template.Library() |
||||
|
||||
|
||||
@register.simple_tag |
||||
def format_date_range(from_date, to_date, variant='short'): |
||||
""" |
||||
>>> import datetime |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,1,20)) |
||||
'15. - 20.01.2009.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,2,20)) |
||||
'15.01. - 20.02.2009.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,2,20)) |
||||
'15.01.2009. - 20.02.2010.' |
||||
>>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,1,20)) |
||||
'15.01.2009. - 20.01.2010.' |
||||
|
||||
Use in django templates: |
||||
|
||||
{% load date_range %} |
||||
{% format_date_range exhibition.start_on exhibition.end_on %} |
||||
""" |
||||
if variant.lower() not in ('short', 'long', ''): |
||||
variant = 'short' |
||||
if variant.endswith("_"): |
||||
variant = variant + "_" |
||||
|
||||
from_format = to_format = get_format(variant.upper() + 'DATE_FORMAT') |
||||
|
||||
if from_date == to_date: |
||||
return date_format(to_date, get_format(to_format)) |
||||
|
||||
if (from_date.year == to_date.year): |
||||
from_format = get_format(variant.upper() + 'DAYMONTH_FORMAT') or 'd/m/' |
||||
if (from_date.month == to_date.month): |
||||
from_format = get_format(variant.upper() + 'DAYONLY_FORMAT') or 'd' |
||||
separator = get_format('DATE_RANGE_SEPARATOR') or "–" |
||||
# import ipdb; ipdb.set_trace() |
||||
|
||||
print from_format, to_format |
||||
|
||||
f = date_format(from_date, get_format(from_format)) |
||||
t = date_format(to_date, get_format(to_format)) |
||||
|
||||
return variant.upper() + " " + separator.join((f, t)) |
||||
|
||||
|
||||
def _test(): |
||||
import doctest |
||||
doctest.testmod() |
||||
|
||||
if __name__ == "__main__": |
||||
_test() |
@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
from django import template |
||||
from django.template.defaultfilters import stringfilter |
||||
from django.utils.html import conditional_escape |
||||
from django.utils.safestring import mark_safe |
||||
from .. import markdown_utils |
||||
|
||||
|
||||
register = template.Library() |
||||
|
||||
|
||||
@register.filter(needs_autoescape=True) |
||||
@stringfilter |
||||
def inline_markdown(text, autoescape=None, **kwargs): |
||||
""" Doesn't wrap the markup in a HTML paragraph. """ |
||||
if autoescape: |
||||
esc = conditional_escape |
||||
else: |
||||
esc = lambda x: x |
||||
return mark_safe(markdown_utils.inline_markdown_processor.convert(esc(text), **kwargs)) |
||||
|
||||
|
||||
@register.filter(needs_autoescape=True) |
||||
@stringfilter |
||||
def markdown(text, autoescape=None, **kwargs): |
||||
if autoescape: |
||||
esc = conditional_escape |
||||
else: |
||||
esc = lambda x: x |
||||
return mark_safe(markdown_utils.markdown_processor.convert(esc(text), **kwargs)) |
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
from django import template |
||||
from django.template.defaultfilters import stringfilter |
||||
from django.utils.html import conditional_escape |
||||
from django.utils.safestring import mark_safe |
||||
|
||||
|
||||
register = template.Library() |
||||
|
||||
|
||||
@register.filter() |
||||
def conditional_punctuation(value, punctuation=",", space=" "): |
||||
""" |
||||
Appends punctuation if the (stripped) value is not empty |
||||
and the value does not already end in a punctuation mark (.,:;!?). |
||||
""" |
||||
value = value.strip() |
||||
if value: |
||||
if value[-1] not in ".,:;!?": |
||||
value += conditional_escape(punctuation) |
||||
value += conditional_escape(space) # Append previously stripped space |
||||
return value |
||||
conditional_punctuation.is_safe = True |
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2014-2015 |
||||
|
||||
from django import template |
||||
from django.db import models |
||||
|
||||
from ..translation import get_translation, get_translated_field |
||||
|
||||
|
||||
register = template.Library() |
||||
|
||||
|
||||
@register.filter |
||||
def translation(obj): |
||||
return get_translation(obj) |
||||
|
||||
|
||||
@register.filter |
||||
def translate(obj, field_name): |
||||
return get_translated_field(obj, field_name) |
||||
|
||||
# Alias |
||||
translated_field = translate |
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
from django.utils.text import slugify |
||||
from django.utils import six |
||||
from django.utils.encoding import force_text |
||||
from django.utils.functional import allow_lazy |
||||
from django.utils.safestring import SafeText |
||||
|
||||
# import unicodedata |
||||
import translitcodec |
||||
import codecs |
||||
|
||||
|
||||
def downgrade(value): |
||||
""" |
||||
Downgrade unicode to ascii, transliterating accented characters. |
||||
""" |
||||
value = force_text(value) |
||||
return codecs.encode(value, 'translit/long') |
||||
downgrade = allow_lazy(downgrade, six.text_type, SafeText) |
||||
|
||||
|
||||
def slugify_long(value): |
||||
value = force_text(value) |
||||
return slugify(downgrade(value)) |
||||
slugify_long = allow_lazy(slugify_long, six.text_type, SafeText) |
||||
|
||||
|
||||
def slugify_german(value): |
||||
""" |
||||
Transliterates Umlaute before calling django's slugify function. |
||||
""" |
||||
umlaute = { |
||||
'Ä': 'Ae', |
||||
'Ö': 'Oe', |
||||
'Ü': 'Ue', |
||||
'ä': 'ae', |
||||
'ö': 'oe', |
||||
'ü': 'ue', |
||||
'ß': 'ss', |
||||
} |
||||
|
||||
value = force_text(value) |
||||
umap = {ord(key): unicode(val) for key, val in umlaute.items()} |
||||
return slugify(value.translate(umap)) |
||||
slugify_german = allow_lazy(slugify_german, six.text_type, SafeText) |
||||
|
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
from django.utils import timezone |
||||
|
||||
|
||||
def smart_default_tz(datetime_value): |
||||
""" |
||||
Returns the give datetime with the default timezone applied. |
||||
""" |
||||
if timezone.is_naive(datetime_value): |
||||
datetime_value = timezone.make_aware(datetime_value, timezone=timezone.get_default_timezone()) |
||||
return timezone.localtime(datetime_value, timezone.get_default_timezone()) |
||||
|
@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*- |
||||
from __future__ import unicode_literals |
||||
# Erik Stein <code@classlibrary.net>, 2015 |
||||
|
||||
import os |
||||
from contextlib import contextmanager |
||||
from django import http |
||||
from django.conf import settings |
||||
from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist |
||||
from django.core.urlresolvers import translate_url |
||||
from django.template.loader import select_template |
||||
from django.utils import translation |
||||
from django.views.generic import TemplateView |
||||
from django.utils.translation import check_for_language, LANGUAGE_SESSION_KEY |
||||
from django.utils.http import is_safe_url |
||||
from django.http import HttpResponseRedirect |
||||
from django.views.i18n import LANGUAGE_QUERY_PARAMETER |
||||
|
||||
|
||||
def lang_suffix(language_code=None): |
||||
""" |
||||
Returns the suffix appropriate for adding to field names for selecting |
||||
the current language. |
||||
""" |
||||
if not language_code: |
||||
language_code = translation.get_language() |
||||
if not language_code: |
||||
language_code = settings.LANGUAGE_CODE |
||||
language_code = language_code[:2] or 'de' # FIXME Fall back to default language |
||||
return "_%s" % language_code |
||||
|
||||
|
||||
class DirectTemplateView(TemplateView): |
||||
extra_context = None |
||||
|
||||
def get_context_data(self, **kwargs): |
||||
context = super(DirectTemplateView, self).get_context_data(**kwargs) |
||||
if self.extra_context is not None: |
||||
for key, value in self.extra_context.items(): |
||||
if callable(value): |
||||
context[key] = value() |
||||
else: |
||||
context[key] = value |
||||
return context |
||||
|
||||
|
||||
class I18nDirectTemplateView(DirectTemplateView): |
||||
def get_template_names(self): |
||||
t_name, t_ext = os.path.splitext(self.template_name) |
||||
lang = translation.get_language() |
||||
template_name = select_template(( |
||||
"%s.%s%s" % (t_name, lang, t_ext), |
||||
self.template_name |
||||
)).name |
||||
return [template_name] |
||||
|
||||
|
||||
def i18n_direct_to_template(request, *args, **kwargs): |
||||
return I18nDirectTemplateView(*args, **kwargs).as_view() |
||||
|
||||
|
||||
def get_translation(obj, relation_name='translations', language_code=None): |
||||
language_code = language_code or translation.get_language()[:2] |
||||
try: |
||||
return getattr(obj, relation_name).get(language=language_code) |
||||
except ObjectDoesNotExist: |
||||
try: |
||||
return getattr(obj, relation_name).get(language=(language_code == 'en' and 'de' or 'en')) |
||||
except ObjectDoesNotExist: |
||||
return None |
||||
|
||||
|
||||
# class FieldTranslationMixin(object): |
||||
# """ |
||||
# If the model has a field `attr` or `attr_<language_code>`, return it's |
||||
# value, else raise ValueError. |
||||
# """ |
||||
|
||||
# def __getattr__(self, attr): |
||||
# if attr in self.__dict__: |
||||
# return self.__dict__[attr] |
||||
# for field in self._meta.multilingual: |
||||
# code = None |
||||
# match = re.match(r'^%s_(?P<code>[a-z_]{2,5})$' % field, str(attr)) |
||||
# if match: |
||||
# code = match.groups('code') |
||||
# code = code[:2] # let's limit it to two letter |
||||
# elif attr in self._meta.multilingual: |
||||
# code = self._language |
||||
# field = attr |
||||
# if code is not None: |
||||
# try: |
||||
# return self._meta.translation.objects.select_related().get(model=self, language__code=code).__dict__[field] |
||||
# except ObjectDoesNotExist: |
||||
# if MULTILINGUAL_FALL_BACK_TO_DEFAULT and MULTILINGUAL_DEFAULT and code != MULTILINGUAL_DEFAULT: |
||||
# try: |
||||
# return self._meta.translation.objects.select_related().get(model=self, language__code=MULTILINGUAL_DEFAULT).__dict__[field] |
||||
# except ObjectDoesNotExist: |
||||
# pass |
||||
# if MULTILINGUAL_FAIL_SILENTLY: |
||||
# return None |
||||
# raise ValueError, "'%s' has no translation in '%s'"%(self, code) |
||||
# raise AttributeError, "'%s' object has no attribute '%s'"%(self.__class__.__name__, str(attr)) |
||||
|
||||
|
||||
def get_translated_field(obj, field_name, language_code=None): |
||||
""" |
||||
Tries to get the model attribute corresponding to the current |
||||
selected language by appending "_<language_code>" to the attribute |
||||
name and returning the value. |
||||
|
||||
On AttributeError try to return the other language or the attribute |
||||
without the language suffix. |
||||
|
||||
If the attribute is empty or null, try to return the value of |
||||
the other language's attribute. |
||||
|
||||
If there is an attribute with the name without any language code |
||||
extension, return the value of this. |
||||
|
||||
Best return value: |
||||
field_name + lang_suffix for current language |
||||
|
||||
If empty or field does not exist: |
||||
if default language and field_name |
||||
field_name |
||||
else |
||||
field_name + lang_suffix other language |
||||
""" |
||||
# TODO Implement multiple languages |
||||
language_code = (language_code or |
||||
translation.get_language() or |
||||
settings.LANGUAGE_CODE)[:2] |
||||
is_default_language = bool(language_code == settings.LANGUAGE_CODE[:2]) |
||||
if language_code == 'de': |
||||
other_language_code = 'en' |
||||
else: |
||||
other_language_code = 'de' |
||||
|
||||
def has_db_field(field_name): |
||||
try: |
||||
# Only try to access database fields to avoid recursion |
||||
obj._meta.get_field(field_name) |
||||
return True |
||||
except FieldDoesNotExist: |
||||
return False |
||||
|
||||
translated_field_name = '%s_%s' % (field_name, language_code) |
||||
other_translated_field_name = '%s_%s' % (field_name, other_language_code) |
||||
rv = "" |
||||
if hasattr(obj, translated_field_name): |
||||
rv = getattr(obj, translated_field_name) |
||||
if not rv: |
||||
if is_default_language and has_db_field(field_name): |
||||
rv = getattr(obj, field_name) |
||||
elif hasattr(obj, other_translated_field_name): |
||||
rv = getattr(obj, other_translated_field_name) |
||||
if not rv and has_db_field(field_name): |
||||
rv = getattr(obj, field_name) |
||||
# FIXME Raise error if neither field exists |
||||
return rv |
||||
|
||||
|
||||
@contextmanager |
||||
def active_language(lang='de'): |
||||
translation.activate(lang) |
||||
yield |
||||
translation.deactivate() |
||||
|
||||
|
||||
def set_language(request): |
||||
""" |
||||
Modified copy from django.views.i18n |
||||
""" |
||||
next = request.POST.get('next', request.GET.get('next')) |
||||
if not is_safe_url(url=next, host=request.get_host()): |
||||
next = request.META.get('HTTP_REFERER') |
||||
if not is_safe_url(url=next, host=request.get_host()): |
||||
next = '/' |
||||
response = http.HttpResponseRedirect(next) |
||||
if request.method == 'GET': |
||||
lang_code = request.GET.get(LANGUAGE_QUERY_PARAMETER, None) |
||||
if lang_code and check_for_language(lang_code): |
||||
next_trans = translate_url(next, lang_code) |
||||
if next_trans != next: |
||||
response = http.HttpResponseRedirect(next_trans) |
||||
|
||||
if hasattr(request, 'session'): |
||||
request.session[LANGUAGE_SESSION_KEY] = lang_code |
||||
else: |
||||
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code, |
||||
max_age=settings.LANGUAGE_COOKIE_AGE, |
||||
path=settings.LANGUAGE_COOKIE_PATH, |
||||
domain=settings.LANGUAGE_COOKIE_DOMAIN) |
||||
return response |
Loading…
Reference in new issue