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
|