1
|
|
|
""" |
2
|
|
|
The configuration wrappers that are used for :ref:`PARLER_LANGUAGES`. |
3
|
|
|
""" |
4
|
|
|
|
5
|
|
|
import copy |
6
|
|
|
import sys |
7
|
|
|
|
8
|
|
|
from django.conf import settings |
9
|
|
|
from django.core.exceptions import ImproperlyConfigured |
10
|
|
|
from django.utils import six |
11
|
|
|
from django.utils.translation import get_language |
12
|
|
|
from parler.utils.i18n import is_supported_django_language |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
def add_default_language_settings(languages_list, var_name='PARLER_LANGUAGES', **extra_defaults): |
16
|
|
|
""" |
17
|
|
|
Apply extra defaults to the language settings. |
18
|
|
|
This function can also be used by other packages to |
19
|
|
|
create their own variation of ``PARLER_LANGUAGES`` with extra fields. |
20
|
|
|
For example:: |
21
|
|
|
|
22
|
|
|
from django.conf import settings |
23
|
|
|
from parler import appsettings as parler_appsettings |
24
|
|
|
|
25
|
|
|
# Create local names, which are based on the global parler settings |
26
|
|
|
MYAPP_DEFAULT_LANGUAGE_CODE = getattr(settings, 'MYAPP_DEFAULT_LANGUAGE_CODE', parler_appsettings.PARLER_DEFAULT_LANGUAGE_CODE) |
27
|
|
|
MYAPP_LANGUAGES = getattr(settings, 'MYAPP_LANGUAGES', parler_appsettings.PARLER_LANGUAGES) |
28
|
|
|
|
29
|
|
|
# Apply the defaults to the languages |
30
|
|
|
MYAPP_LANGUAGES = parler_appsettings.add_default_language_settings(MYAPP_LANGUAGES, 'MYAPP_LANGUAGES', |
31
|
|
|
code=MYAPP_DEFAULT_LANGUAGE_CODE, |
32
|
|
|
fallback=MYAPP_DEFAULT_LANGUAGE_CODE, |
33
|
|
|
hide_untranslated=False |
34
|
|
|
) |
35
|
|
|
|
36
|
|
|
The returned object will be an :class:`~parler.utils.conf.LanguagesSetting` object, |
37
|
|
|
which adds additional methods to the :class:`dict` object. |
38
|
|
|
|
39
|
|
|
:param languages_list: The settings, in :ref:`PARLER_LANGUAGES` format. |
40
|
|
|
:param var_name: The name of your variable, for debugging output. |
41
|
|
|
:param extra_defaults: Any defaults to override in the ``languages_list['default']`` section, e.g. ``code``, ``fallback``, ``hide_untranslated``. |
42
|
|
|
:return: The updated ``languages_list`` with all defaults applied to all sections. |
43
|
|
|
:rtype: LanguagesSetting |
44
|
|
|
""" |
45
|
|
|
languages_list = LanguagesSetting(languages_list) |
46
|
|
|
|
47
|
|
|
languages_list.setdefault('default', {}) |
48
|
|
|
defaults = languages_list['default'] |
49
|
|
|
defaults.setdefault('hide_untranslated', False) # Whether queries with .active_translations() may or may not return the fallback language. |
50
|
|
|
|
51
|
|
|
if 'fallback' in defaults: |
52
|
|
|
#warnings.warn("Please use 'fallbacks' instead of 'fallback' in the 'defaults' section of {0}".format(var_name), DeprecationWarning) |
53
|
|
|
defaults['fallbacks'] = [defaults.pop('fallback')] |
54
|
|
|
if 'fallback' in extra_defaults: |
55
|
|
|
#warnings.warn("Please use 'fallbacks' instead of 'fallback' in parameters for {0} = add_default_language_settings(..)".format(var_name), DeprecationWarning) |
56
|
|
|
extra_defaults['fallbacks'] = [extra_defaults.pop('fallback')] |
57
|
|
|
|
58
|
|
|
defaults.update(extra_defaults) # Also allow to override code and fallback this way. |
59
|
|
|
|
60
|
|
|
# This function previously existed in appsettings, where it could reference the defaults directly. |
61
|
|
|
# However, this module is a more logical place for this function. To avoid circular import problems, |
62
|
|
|
# the 'code' and 'fallback' parameters are always passed by the appsettings module. |
63
|
|
|
# In case these are missing, default to the original behavior for backwards compatibility. |
64
|
|
|
if 'code' not in defaults: |
65
|
|
|
from parler import appsettings |
66
|
|
|
defaults['code'] = appsettings.PARLER_DEFAULT_LANGUAGE_CODE |
67
|
|
|
if 'fallbacks' not in defaults: |
68
|
|
|
from parler import appsettings |
69
|
|
|
defaults['fallbacks'] = [appsettings.PARLER_DEFAULT_LANGUAGE_CODE] |
70
|
|
|
|
71
|
|
|
if not is_supported_django_language(defaults['code']): |
72
|
|
|
raise ImproperlyConfigured("The value for {0}['defaults']['code'] ('{1}') does not exist in LANGUAGES".format(var_name, defaults['code'])) |
73
|
|
|
|
74
|
|
|
for site_id, lang_choices in six.iteritems(languages_list): |
75
|
|
|
if site_id == 'default': |
76
|
|
|
continue |
77
|
|
|
|
78
|
|
|
if not isinstance(lang_choices, (list, tuple)): |
79
|
|
|
raise ImproperlyConfigured("{0}[{1}] should be a tuple of language choices!".format(var_name, site_id)) |
80
|
|
|
for i, choice in enumerate(lang_choices): |
81
|
|
|
if not is_supported_django_language(choice['code']): |
82
|
|
|
raise ImproperlyConfigured("{0}[{1}][{2}]['code'] does not exist in LANGUAGES".format(var_name, site_id, i)) |
83
|
|
|
|
84
|
|
|
# Copy all items from the defaults, so you can provide new fields too. |
85
|
|
|
for key, value in six.iteritems(defaults): |
86
|
|
|
choice.setdefault(key, value) |
87
|
|
|
|
88
|
|
|
return languages_list |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
class LanguagesSetting(dict): |
92
|
|
|
""" |
93
|
|
|
This is the actual object type of the :ref:`PARLER_LANGUAGES` setting. |
94
|
|
|
Besides the regular :class:`dict` behavior, it also adds some additional methods. |
95
|
|
|
""" |
96
|
|
|
|
97
|
|
|
def get_language(self, language_code, site_id=None): |
98
|
|
|
""" |
99
|
|
|
Return the language settings for the current site |
100
|
|
|
|
101
|
|
|
This function can be used with other settings variables |
102
|
|
|
to support modules which create their own variation of the ``PARLER_LANGUAGES`` setting. |
103
|
|
|
For an example, see :func:`~parler.appsettings.add_default_language_settings`. |
104
|
|
|
""" |
105
|
|
|
if language_code is None: |
106
|
|
|
# This happens when using parler in management commands. |
107
|
|
|
# Use translation.activate('en') if you need to have a default locale active. |
108
|
|
|
if get_language() is None: |
109
|
|
|
raise ValueError("language_code can't be null, use translation.activate(..) when accessing translated models outside the request/response loop.") |
110
|
|
|
else: |
111
|
|
|
raise ValueError("language_code can't be null") |
112
|
|
|
|
113
|
|
|
if site_id is None: |
114
|
|
|
site_id = getattr(settings, 'SITE_ID', None) |
115
|
|
|
|
116
|
|
|
for lang_dict in self.get(site_id, ()): |
117
|
|
|
if lang_dict['code'] == language_code: |
118
|
|
|
return lang_dict |
119
|
|
|
|
120
|
|
|
# no language match, search for variant: fr-ca falls back to fr |
121
|
|
|
for lang_dict in self.get(site_id, ()): |
122
|
|
|
if lang_dict['code'].split('-')[0] == language_code.split('-')[0]: |
123
|
|
|
return lang_dict |
124
|
|
|
|
125
|
|
|
return self['default'] |
126
|
|
|
|
127
|
|
|
def get_active_choices(self, language_code=None, site_id=None): |
128
|
|
|
""" |
129
|
|
|
Find out which translations should be visible in the site. |
130
|
|
|
It returns a list with either a single choice (the current language), |
131
|
|
|
or a list with the current language + fallback language. |
132
|
|
|
""" |
133
|
|
|
if language_code is None: |
134
|
|
|
language_code = get_language() |
135
|
|
|
|
136
|
|
|
lang_dict = self.get_language(language_code, site_id=site_id) |
137
|
|
|
if not lang_dict['hide_untranslated']: |
138
|
|
|
return [language_code] + [lang for lang in lang_dict['fallbacks'] if lang != language_code] |
139
|
|
|
else: |
140
|
|
|
return [language_code] |
141
|
|
|
|
142
|
|
|
def get_fallback_languages(self, language_code=None, site_id=None): |
143
|
|
|
""" |
144
|
|
|
Find out what the fallback language is for a given language choice. |
145
|
|
|
|
146
|
|
|
.. versionadded 1.5 |
147
|
|
|
""" |
148
|
|
|
choices = self.get_active_choices(language_code, site_id=site_id) |
149
|
|
|
return choices[1:] |
150
|
|
|
|
151
|
|
|
def get_fallback_language(self, language_code=None, site_id=None): |
152
|
|
|
""" |
153
|
|
|
Find out what the fallback language is for a given language choice. |
154
|
|
|
|
155
|
|
|
.. deprecated:: 1.5 |
156
|
|
|
Use :func:`get_fallback_languages` instead. |
157
|
|
|
""" |
158
|
|
|
choices = self.get_active_choices(language_code, site_id=site_id) |
159
|
|
|
if choices and len(choices) > 1: |
160
|
|
|
# Still take the last, like previous code. |
161
|
|
|
# With multiple fallback languages that means taking the base language. |
162
|
|
|
# Hence, upgrade the code to use get_fallback_languages() instead. |
163
|
|
|
return choices[-1] |
164
|
|
|
else: |
165
|
|
|
return None |
166
|
|
|
|
167
|
|
|
def get_default_language(self): |
168
|
|
|
""" |
169
|
|
|
Return the default language. |
170
|
|
|
""" |
171
|
|
|
return self['default']['code'] |
172
|
|
|
|
173
|
|
|
def get_first_language(self, site_id=None): |
174
|
|
|
""" |
175
|
|
|
Return the first language for the current site. |
176
|
|
|
This can be used for user interfaces, where the languages are displayed in tabs. |
177
|
|
|
""" |
178
|
|
|
if site_id is None: |
179
|
|
|
site_id = getattr(settings, 'SITE_ID', None) |
180
|
|
|
|
181
|
|
|
try: |
182
|
|
|
return self[site_id][0]['code'] |
183
|
|
|
except (KeyError, IndexError): |
184
|
|
|
# No configuration, always fallback to default language. |
185
|
|
|
# This is essentially a non-multilingual configuration. |
186
|
|
|
return self['default']['code'] |
187
|
|
|
|
188
|
|
|
|
189
|
|
|
def get_parler_languages_from_django_cms(cms_languages=None): |
190
|
|
|
""" |
191
|
|
|
Converts django CMS' setting CMS_LANGUAGES into PARLER_LANGUAGES. Since |
192
|
|
|
CMS_LANGUAGES is a strict superset of PARLER_LANGUAGES, we do a bit of |
193
|
|
|
cleansing to remove irrelevant items. |
194
|
|
|
""" |
195
|
|
|
valid_keys = ['code', 'fallbacks', 'hide_untranslated', |
196
|
|
|
'redirect_on_fallback'] |
197
|
|
|
if cms_languages: |
198
|
|
|
if sys.version_info < (3, 0, 0): |
199
|
|
|
int_types = (int, long) |
200
|
|
|
else: |
201
|
|
|
int_types = int |
202
|
|
|
|
203
|
|
|
parler_languages = copy.deepcopy(cms_languages) |
204
|
|
|
for site_id, site_config in cms_languages.items(): |
205
|
|
|
if site_id and ( |
206
|
|
|
not isinstance(site_id, int_types) and |
207
|
|
|
site_id != 'default' |
208
|
|
|
): |
209
|
|
|
del parler_languages[site_id] |
210
|
|
|
continue |
211
|
|
|
|
212
|
|
|
if site_id == 'default': |
213
|
|
|
for key, value in site_config.items(): |
214
|
|
|
if key not in valid_keys: |
215
|
|
|
del parler_languages['default'][key] |
216
|
|
|
else: |
217
|
|
|
for i, lang_config in enumerate(site_config): |
218
|
|
|
for key, value in lang_config.items(): |
219
|
|
|
if key not in valid_keys: |
220
|
|
|
del parler_languages[site_id][i][key] |
221
|
|
|
return parler_languages |
222
|
|
|
return None |
223
|
|
|
|