1# -*- coding: utf-8 -*-
2from contextlib import contextmanager
3
4from django.conf import settings
5from django.utils import translation
6from django.utils.translation import ugettext_lazy as _
7from django.urls import get_resolver
8
9from cms.exceptions import LanguageError
10from cms.utils.compat.dj import LocalePrefixPattern
11from cms.utils.conf import get_cms_setting, get_site_id
12
13
14@contextmanager
15def force_language(new_lang):
16    old_lang = get_current_language()
17    if old_lang != new_lang:
18        translation.activate(new_lang)
19    yield
20    translation.activate(old_lang)
21
22
23def get_languages(site_id=None):
24    site_id = get_site_id(site_id)
25    result = get_cms_setting('LANGUAGES').get(site_id)
26    if not result:
27        result = []
28        defaults = get_cms_setting('LANGUAGES').get('default', {})
29        for code, name in settings.LANGUAGES:
30            lang = {'code': code, 'name': _(name)}
31            lang.update(defaults)
32            result.append(lang)
33        get_cms_setting('LANGUAGES')[site_id] = result
34    return result
35
36
37def get_site_language_from_request(request, site_id=None):
38    from cms.utils import get_current_site
39
40    language = request.GET.get('language', None)
41
42    if site_id is None:
43        site_id = get_current_site().pk
44
45    if is_valid_site_language(language, site_id=site_id):
46        return language
47
48    language = getattr(request, 'LANGUAGE_CODE', None)
49
50    if is_valid_site_language(language, site_id=site_id):
51        return language
52    return get_default_language_for_site(site_id=site_id)
53
54
55def get_language_code(language_code, site_id=None):
56    """
57    Returns language code while making sure it's in LANGUAGES
58    """
59    if not language_code:
60        return None
61
62    languages = get_language_list(site_id)
63
64    if language_code in languages: # direct hit
65        return language_code
66
67    for lang in languages:
68        if language_code.split('-')[0] == lang: # base language hit
69            return lang
70        if lang.split('-')[0] == language_code: # base language hit
71            return lang
72    return language_code
73
74
75def get_current_language():
76    """
77    Returns the currently active language
78
79    It's a replacement for Django's translation.get_language() to make sure the LANGUAGE_CODE will be found in LANGUAGES.
80    Overcomes this issue: https://code.djangoproject.com/ticket/9340
81    """
82    language_code = translation.get_language()
83    return get_language_code(language_code)
84
85
86def get_language_list(site_id=None):
87    """
88    :return: returns a list of iso2codes for this site
89    """
90    return ([lang['code'] for lang in get_languages(site_id)] if settings.USE_I18N
91            else [settings.LANGUAGE_CODE])
92
93
94def get_language_tuple(site_id=None):
95    """
96    :return: returns an list of tuples like the old CMS_LANGUAGES or the LANGUAGES for this site
97    """
98    return [(lang['code'], lang['name']) for lang in get_languages(site_id)]
99
100
101def get_language_dict(site_id=None):
102    """
103    :return: returns an dict of cms languages
104    """
105    return dict(get_language_tuple(site_id))
106
107
108def get_public_languages(site_id=None):
109    """
110    :return: list of iso2codes of public languages for this site
111    """
112    return [lang['code'] for lang in get_language_objects(site_id)
113            if lang.get('public', True)]
114
115
116def get_language_object(language_code, site_id=None):
117    """
118    :param language_code: RFC5646 language code
119    :return: the language object filled up by defaults
120    """
121    for language in get_languages(site_id):
122        if language['code'] == get_language_code(language_code):
123            return language
124    raise LanguageError('Language not found: %s' % language_code)
125
126
127def get_language_objects(site_id=None):
128    """
129    returns list of all language objects filled up by default values
130    """
131    return list(get_languages(site_id))
132
133
134def get_default_language(language_code=None, site_id=None):
135    """
136    Returns default language depending on settings.LANGUAGE_CODE merged with
137    best match from get_cms_setting('LANGUAGES')
138
139    Returns: language_code
140    """
141    if not language_code:
142        language_code = get_language_code(settings.LANGUAGE_CODE)
143
144    languages = get_language_list(site_id)
145
146    # first try if there is an exact language
147    if language_code in languages:
148        return language_code
149
150    # otherwise split the language code if possible, so iso3
151    language_code = language_code.split("-")[0]
152
153    if not language_code in languages:
154        return settings.LANGUAGE_CODE
155
156    return language_code
157
158
159def get_default_language_for_site(site_id):
160    return get_language_list(site_id)[0]
161
162
163def get_fallback_languages(language, site_id=None):
164    """
165    returns a list of fallback languages for the given language
166    """
167    try:
168        language = get_language_object(language, site_id)
169    except LanguageError:
170        language = get_languages(site_id)[0]
171    return language.get('fallbacks', [])
172
173
174def get_redirect_on_fallback(language, site_id=None):
175    """
176    returns if you should redirect on language fallback
177    :param language:
178    :param site_id:
179    :return: Boolean
180    """
181    language = get_language_object(language, site_id)
182    return language.get('redirect_on_fallback', True)
183
184
185def hide_untranslated(language, site_id=None):
186    """
187    Should untranslated pages in this language be hidden?
188    :param language:
189    :param site_id:
190    :return: A Boolean
191    """
192    obj = get_language_object(language, site_id)
193    return obj.get('hide_untranslated', True)
194
195
196def is_language_prefix_patterns_used():
197    """
198    Returns `True` if the `LocaleRegexURLResolver` or `LocalePrefixPattern` is
199    used at root level of the urlpatterns, else it returns `False`.
200    """
201    return any(
202        isinstance(
203            getattr(url_pattern, 'pattern', url_pattern),
204            LocalePrefixPattern
205        )
206        for url_pattern in get_resolver(None).url_patterns
207    )
208
209
210def is_valid_site_language(language, site_id):
211    return language in get_language_list(site_id)
212