Completed
Push — master ( a6fb63...42f11c )
by Diederik van der
57s
created

parler.utils.LanguagesSetting.get_active_choices()   B

Complexity

Conditions 5

Size

Total Lines 14

Duplication

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