You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
7.6 KiB
211 lines
7.6 KiB
# -*- 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.http import HttpResponseRedirect |
|
from django.template.loader import select_template |
|
from django.utils import translation |
|
from django.utils.http import is_safe_url |
|
from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit |
|
from django.utils.translation import check_for_language, LANGUAGE_SESSION_KEY |
|
from django.views.generic import TemplateView |
|
from django.views.i18n import LANGUAGE_QUERY_PARAMETER |
|
|
|
|
|
FALLBACK_LANGUAGE_CODE = getattr(settings, 'FALLBACK_LANGUAGE_CODE', 'en') |
|
|
|
|
|
def _normalize_language_code(language_code): |
|
""" |
|
Makes sure the language code is not an empty string. |
|
""" |
|
return ( |
|
language_code or |
|
translation.get_language() or |
|
settings.LANGUAGE_CODE or |
|
FALLBACK_LANGUAGE_CODE |
|
) |
|
|
|
|
|
def get_language(language_code=None): |
|
return _normalize_language_code(language_code)[:2] |
|
|
|
|
|
def lang_suffix(language_code=None): |
|
""" |
|
Returns the suffix appropriate for adding to field names for selecting |
|
the current language. |
|
""" |
|
language_code = _normalize_language_code(language_code)[:2] |
|
return "_{}".format(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 = _normalize_language_code(language_code)[:2] |
|
try: |
|
return getattr(obj, relation_name).get(language=language_code) |
|
except ObjectDoesNotExist: |
|
# FIXME Fetch best possible language from settings.LANGUAGES |
|
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 = _normalize_language_code(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_get(request): |
|
""" |
|
set_language per GET request, |
|
modified copy from django.views.i18n (django 1.9.x) |
|
""" |
|
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
|
|
|