File: conf.py

package info (click to toggle)
python-django-parler 2.3-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,012 kB
  • sloc: python: 4,291; makefile: 164; sh: 6
file content (221 lines) | stat: -rw-r--r-- 9,178 bytes parent folder | download | duplicates (2)
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
"""
The configuration wrappers that are used for :ref:`PARLER_LANGUAGES`.
"""

import copy
import sys

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured

from parler.utils.i18n import get_language, get_null_language_error, is_supported_django_language


def add_default_language_settings(languages_list, var_name="PARLER_LANGUAGES", **extra_defaults):
    """
    Apply extra defaults to the language settings.
    This function can also be used by other packages to
    create their own variation of ``PARLER_LANGUAGES`` with extra fields.
    For example::

        from django.conf import settings
        from parler import appsettings as parler_appsettings

        # Create local names, which are based on the global parler settings
        MYAPP_DEFAULT_LANGUAGE_CODE = getattr(settings, 'MYAPP_DEFAULT_LANGUAGE_CODE', parler_appsettings.PARLER_DEFAULT_LANGUAGE_CODE)
        MYAPP_LANGUAGES = getattr(settings, 'MYAPP_LANGUAGES', parler_appsettings.PARLER_LANGUAGES)

        # Apply the defaults to the languages
        MYAPP_LANGUAGES = parler_appsettings.add_default_language_settings(MYAPP_LANGUAGES, 'MYAPP_LANGUAGES',
            code=MYAPP_DEFAULT_LANGUAGE_CODE,
            fallback=MYAPP_DEFAULT_LANGUAGE_CODE,
            hide_untranslated=False
        )

    The returned object will be an :class:`~parler.utils.conf.LanguagesSetting` object,
    which adds additional methods to the :class:`dict` object.

    :param languages_list: The settings, in :ref:`PARLER_LANGUAGES` format.
    :param var_name: The name of your variable, for debugging output.
    :param extra_defaults: Any defaults to override in the ``languages_list['default']`` section, e.g. ``code``, ``fallback``, ``hide_untranslated``.
    :return: The updated ``languages_list`` with all defaults applied to all sections.
    :rtype: LanguagesSetting
    """
    languages_list = LanguagesSetting(languages_list)

    languages_list.setdefault("default", {})
    defaults = languages_list["default"]
    defaults.setdefault(
        "hide_untranslated", False
    )  # Whether queries with .active_translations() may or may not return the fallback language.

    if "fallback" in defaults:
        # warnings.warn("Please use 'fallbacks' instead of 'fallback' in the 'defaults' section of {0}".format(var_name), DeprecationWarning)
        defaults["fallbacks"] = [defaults.pop("fallback")]
    if "fallback" in extra_defaults:
        # warnings.warn("Please use 'fallbacks' instead of 'fallback' in parameters for {0} = add_default_language_settings(..)".format(var_name), DeprecationWarning)
        extra_defaults["fallbacks"] = [extra_defaults.pop("fallback")]

    defaults.update(extra_defaults)  # Also allow to override code and fallback this way.

    # This function previously existed in appsettings, where it could reference the defaults directly.
    # However, this module is a more logical place for this function. To avoid circular import problems,
    # the 'code' and 'fallback' parameters are always passed by the appsettings module.
    # In case these are missing, default to the original behavior for backwards compatibility.
    if "code" not in defaults:
        from parler import appsettings

        defaults["code"] = appsettings.PARLER_DEFAULT_LANGUAGE_CODE
    if "fallbacks" not in defaults:
        from parler import appsettings

        defaults["fallbacks"] = [appsettings.PARLER_DEFAULT_LANGUAGE_CODE]

    if not is_supported_django_language(defaults["code"]):
        raise ImproperlyConfigured(
            "The value for {}['defaults']['code'] ('{}') does not exist in LANGUAGES".format(
                var_name, defaults["code"]
            )
        )

    for site_id, lang_choices in languages_list.items():
        if site_id == "default":
            continue

        if not isinstance(lang_choices, (list, tuple)):
            raise ImproperlyConfigured(
                f"{var_name}[{site_id}] should be a tuple of language choices!"
            )
        for i, choice in enumerate(lang_choices):
            if not is_supported_django_language(choice["code"]):
                raise ImproperlyConfigured(
                    f"{var_name}[{site_id}][{i}]['code'] does not exist in LANGUAGES"
                )

            # Copy all items from the defaults, so you can provide new fields too.
            for key, value in defaults.items():
                choice.setdefault(key, value)

    return languages_list


class LanguagesSetting(dict):
    """
    This is the actual object type of the :ref:`PARLER_LANGUAGES` setting.
    Besides the regular :class:`dict` behavior, it also adds some additional methods.
    """

    def get_language(self, language_code, site_id=None):
        """
        Return the language settings for the current site

        This function can be used with other settings variables
        to support modules which create their own variation of the ``PARLER_LANGUAGES`` setting.
        For an example, see :func:`~parler.appsettings.add_default_language_settings`.
        """
        if language_code is None:
            raise ValueError(get_null_language_error())

        if site_id is None:
            site_id = getattr(settings, "SITE_ID", None)

        for lang_dict in self.get(site_id, ()):
            if lang_dict["code"] == language_code:
                return lang_dict

        # no language match, search for variant: fr-ca falls back to fr
        for lang_dict in self.get(site_id, ()):
            if lang_dict["code"].split("-")[0] == language_code.split("-")[0]:
                return lang_dict

        return self["default"]

    def get_active_choices(self, language_code=None, site_id=None):
        """
        Find out which translations should be visible in the site.
        It returns a list with either a single choice (the current language),
        or a list with the current language + fallback language.
        """
        if language_code is None:
            language_code = get_language()

        lang_dict = self.get_language(language_code, site_id=site_id)
        if not lang_dict["hide_untranslated"]:
            return [language_code] + [
                lang for lang in lang_dict["fallbacks"] if lang != language_code
            ]
        else:
            return [language_code]

    def get_fallback_languages(self, language_code=None, site_id=None):
        """
        Find out what the fallback language is for a given language choice.

        .. versionadded 1.5
        """
        choices = self.get_active_choices(language_code, site_id=site_id)
        return choices[1:]

    def get_fallback_language(self, language_code=None, site_id=None):
        """
        Find out what the fallback language is for a given language choice.

        .. deprecated:: 1.5
           Use :func:`get_fallback_languages` instead.
        """
        choices = self.get_active_choices(language_code, site_id=site_id)
        if choices and len(choices) > 1:
            # Still take the last, like previous code.
            # With multiple fallback languages that means taking the base language.
            # Hence, upgrade the code to use get_fallback_languages() instead.
            return choices[-1]
        else:
            return None

    def get_default_language(self):
        """
        Return the default language.
        """
        return self["default"]["code"]

    def get_first_language(self, site_id=None):
        """
        Return the first language for the current site.
        This can be used for user interfaces, where the languages are displayed in tabs.
        """
        if site_id is None:
            site_id = getattr(settings, "SITE_ID", None)

        try:
            return self[site_id][0]["code"]
        except (KeyError, IndexError):
            # No configuration, always fallback to default language.
            # This is essentially a non-multilingual configuration.
            return self["default"]["code"]


def get_parler_languages_from_django_cms(cms_languages=None):
    """
    Converts django CMS' setting CMS_LANGUAGES into PARLER_LANGUAGES. Since
    CMS_LANGUAGES is a strict superset of PARLER_LANGUAGES, we do a bit of
    cleansing to remove irrelevant items.
    """
    valid_keys = ["code", "fallbacks", "hide_untranslated", "redirect_on_fallback"]
    if cms_languages:
        parler_languages = copy.deepcopy(cms_languages)
        for site_id, site_config in cms_languages.items():
            if site_id and (not isinstance(site_id, int) and site_id != "default"):
                del parler_languages[site_id]
                continue

            if site_id == "default":
                for key, value in site_config.items():
                    if key not in valid_keys:
                        del parler_languages["default"][key]
            else:
                for i, lang_config in enumerate(site_config):
                    for key, value in lang_config.items():
                        if key not in valid_keys:
                            del parler_languages[site_id][i][key]
        return parler_languages
    return None