-
Notifications
You must be signed in to change notification settings - Fork 238
Expand file tree
/
Copy pathtranslations.py
More file actions
312 lines (258 loc) · 12.1 KB
/
translations.py
File metadata and controls
312 lines (258 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------
"""
This extension adds a language field to every page. When calling the request
processors the page's language is activated.
Pages in secondary languages can be said to be a translation of a page in the
primary language (the first language in settings.LANGUAGES), thereby enabling
deeplinks between translated pages.
It is recommended to activate
:class:`django.middleware.locale.LocaleMiddleware` so that the correct language
will be activated per user or session even for non-FeinCMS managed views such
as Django's administration tool.
"""
from __future__ import absolute_import, unicode_literals
# ------------------------------------------------------------------------
import logging
from django.conf import settings as django_settings
from django.db import models
from django.http import HttpResponseRedirect
from django.utils import translation
from django.utils.html import mark_safe
from django.utils.translation import ugettext_lazy as _
from feincms import extensions, settings
from feincms.translations import is_primary_language
from feincms._internal import monkeypatch_method, monkeypatch_property
# ------------------------------------------------------------------------
logger = logging.getLogger(__name__)
LANGUAGE_COOKIE_NAME = django_settings.LANGUAGE_COOKIE_NAME
if hasattr(translation, 'LANGUAGE_SESSION_KEY'):
LANGUAGE_SESSION_KEY = translation.LANGUAGE_SESSION_KEY
else:
# Django 1.6
LANGUAGE_SESSION_KEY = LANGUAGE_COOKIE_NAME
# ------------------------------------------------------------------------
def user_has_language_set(request):
"""
Determine whether the user has explicitely set a language earlier on.
This is taken later on as an indication that we should not mess with the
site's language settings, after all, the user's decision is what counts.
"""
if (hasattr(request, 'session') and
request.session.get(LANGUAGE_SESSION_KEY) is not None):
return True
if LANGUAGE_COOKIE_NAME in request.COOKIES:
return True
return False
# ------------------------------------------------------------------------
def translation_allowed_language(select_language):
"Check for feincms specific set of allowed front end languages."
if settings.FEINCMS_FRONTEND_LANGUAGES:
language = select_language[:2]
if language not in settings.FEINCMS_FRONTEND_LANGUAGES:
select_language = django_settings.LANGUAGES[0][0]
return select_language
# ------------------------------------------------------------------------
def translation_set_language(request, select_language):
"""
Set and activate a language, if that language is available.
"""
select_language = translation_allowed_language(select_language)
if translation.check_for_language(select_language):
fallback = False
else:
# The page is in a language that Django has no messages for.
# We display anyhow, but fall back to primary language for
# other messages and other applications. It is *highly* recommended to
# create a new django.po for the language instead of
# using this behaviour.
select_language = django_settings.LANGUAGES[0][0]
fallback = True
translation.activate(select_language)
request.LANGUAGE_CODE = translation.get_language()
if hasattr(request, 'session'):
# User has a session, then set this language there
if select_language != request.session.get(LANGUAGE_SESSION_KEY):
request.session[LANGUAGE_SESSION_KEY] = select_language
elif request.method == 'GET' and not fallback:
# No session is active. We need to set a cookie for the language
# so that it persists when users change their location to somewhere
# not under the control of the CMS.
# Only do this when request method is GET (mainly, do not abort
# POST requests)
response = HttpResponseRedirect(request.get_full_path())
response.set_cookie(
str(LANGUAGE_COOKIE_NAME), select_language)
return response
# ------------------------------------------------------------------------
def translations_request_processor_explicit(page, request):
# If this page is just a redirect, don't do any language specific setup
if page.redirect_to:
return
# Until further notice, the user might be wanting to switch to the
# page's language...
desired_language = page.language
# ...except if the user explicitely wants to switch language
if 'set_language' in request.GET:
desired_language = request.GET['set_language']
# ...or the user already has explicitely set a language, bail out and
# don't change it for them behind their back
elif user_has_language_set(request):
return
return translation_set_language(request, desired_language)
# ------------------------------------------------------------------------
def translations_request_processor_standard(page, request):
# If this page is just a redirect, don't do any language specific setup
if getattr(page, 'redirect_to', None):
return
if page.language == translation.get_language():
return
return translation_set_language(request, page.language)
# ------------------------------------------------------------------------
def get_current_language_code(request):
language_code = getattr(request, 'LANGUAGE_CODE', None)
if language_code is None:
logger.warning(
"Could not access request.LANGUAGE_CODE. Is 'django.middleware."
"locale.LocaleMiddleware' in MIDDLEWARE_CLASSES?")
return language_code
# ------------------------------------------------------------------------
class Extension(extensions.Extension):
def handle_model(self):
cls = self.model
cls.add_to_class(
'language',
models.CharField(
_('language'),
max_length=10,
choices=django_settings.LANGUAGES,
default=django_settings.LANGUAGES[0][0]))
cls.add_to_class(
'translation_of',
models.ForeignKey(
'self',
on_delete=models.CASCADE,
blank=True, null=True, verbose_name=_('translation of'),
related_name='translations',
limit_choices_to={'language': django_settings.LANGUAGES[0][0]},
help_text=_(
'Leave this empty for entries in the primary language.'),
)
)
if hasattr(cls, 'register_request_processor'):
if settings.FEINCMS_TRANSLATION_POLICY == "EXPLICIT":
cls.register_request_processor(
translations_request_processor_explicit,
key='translations')
else: # STANDARD
cls.register_request_processor(
translations_request_processor_standard,
key='translations')
if hasattr(cls, 'get_redirect_to_target'):
original_get_redirect_to_target = cls.get_redirect_to_target
@monkeypatch_method(cls)
def get_redirect_to_target(self, request=None):
"""
Find an acceptable redirect target. If this is a local link,
then try to find the page this redirect references and
translate it according to the user's language. This way, one
can easily implement a localized "/"-url to welcome page
redirection.
"""
target = original_get_redirect_to_target(self, request)
if target and target.find('//') == -1:
# Not an offsite link http://bla/blubb
try:
page = cls.objects.page_for_path(target)
language = get_current_language_code(request)
language = translation_allowed_language(language)
page = page.get_translation(language)
# Note: Does not care about active status?
target = page.get_absolute_url()
except cls.DoesNotExist:
pass
return target
@monkeypatch_method(cls)
def available_translations(self):
if not self.id: # New, unsaved pages have no translations
return []
if hasattr(cls.objects, 'apply_active_filters'):
filter_active = cls.objects.apply_active_filters
else:
def filter_active(queryset):
return queryset
if is_primary_language(self.language):
return filter_active(self.translations.all())
elif self.translation_of:
# reuse prefetched queryset, do not filter it
res = [
t for t
in filter_active(self.translation_of.translations.all())
if t.language != self.language]
res.insert(0, self.translation_of)
return res
else:
return []
@monkeypatch_method(cls)
def get_original_translation(self, *args, **kwargs):
if is_primary_language(self.language):
return self
if self.translation_of:
return self.translation_of
logger.debug(
"Page pk=%d (%s) has no primary language translation (%s)",
self.pk, self.language, django_settings.LANGUAGES[0][0])
return self
@monkeypatch_property(cls)
def original_translation(self):
return self.get_original_translation()
@monkeypatch_method(cls)
def get_translation(self, language):
return self.original_translation.translations.get(
language=language)
def handle_modeladmin(self, modeladmin):
extensions.prefetch_modeladmin_get_queryset(
modeladmin, 'translation_of__translations', 'translations')
def available_translations_admin(self, page):
# Do not use available_translations() because we don't care
# whether pages are active or not here.
translations = [page]
translations.extend(page.translations.all())
if page.translation_of:
translations.append(page.translation_of)
translations.extend(page.translation_of.translations.all())
translations = {
p.language: p.id
for p in translations
}
links = []
for key, title in django_settings.LANGUAGES:
if key == page.language:
continue
if key in translations:
links.append('<a href="%s/" title="%s">%s</a>' % (
translations[key], _('Edit translation'), key.upper()))
else:
links.append(
'<a style="color:#baa" href="add/?translation_of='
'%s&language=%s" title="%s">%s</a>' % (
page.id,
key,
_('Create translation'),
key.upper()
)
)
return mark_safe(' | '.join(links))
available_translations_admin.short_description = _('translations')
modeladmin.__class__.available_translations_admin =\
available_translations_admin
if hasattr(modeladmin, 'add_extension_options'):
modeladmin.add_extension_options('language', 'translation_of')
modeladmin.extend_list(
'list_display',
['language', 'available_translations_admin'],
)
modeladmin.extend_list('list_filter', ['language'])
modeladmin.extend_list('raw_id_fields', ['translation_of'])
# ------------------------------------------------------------------------