dynamicweb/cms/utils/i18n.py

255 lines
7.8 KiB
Python

from contextlib import contextmanager
from django.conf import settings
from django.urls import LocalePrefixPattern, get_resolver
from django.utils import translation
from django.utils.translation import gettext_lazy as _
from cms.exceptions import LanguageError
from cms.utils.compat.warnings import RemovedInDjangoCMS60Warning
from cms.utils.conf import get_cms_setting, get_site_id
def _ensure_site_id(site_id, name):
"""
Ensure that the site_id is an integer.
"""
if site_id is None:
import warnings
warnings.warn(
f"{name} called without specifying 'site_id'. This may lead to unexpected behavior. "
f"Call {name} with 'site_id=<some_id>' instead.", RemovedInDjangoCMS60Warning, stacklevel=3
)
return get_site_id(None)
return site_id
@contextmanager
def force_language(new_lang):
old_lang = get_current_language()
if old_lang != new_lang:
translation.activate(new_lang)
yield
translation.activate(old_lang)
def get_languages(site_id=None):
site_id = _ensure_site_id(site_id, "get_languages")
site_id = get_site_id(site_id)
result = get_cms_setting('LANGUAGES').get(site_id)
if not result:
result = []
defaults = get_cms_setting('LANGUAGES').get('default', {})
for code, name in settings.LANGUAGES:
lang = {'code': code, 'name': _(name)}
lang.update(defaults)
result.append(lang)
get_cms_setting('LANGUAGES')[site_id] = result
return result
def get_site_language_from_request(request, site_id=None):
from cms.utils import get_current_site
if site_id is None:
site_id = get_current_site(request).pk
# Level 1: language get parameter
language = request.GET.get('language', None)
if is_valid_site_language(language, site_id=site_id):
return language
# Level 2: LANGUAGE_CODE request parameter
language = getattr(request, 'LANGUAGE_CODE', None)
if is_valid_site_language(language, site_id=site_id):
return language
# Last resort: default language
return get_default_language_for_site(site_id=site_id)
def get_language_code(language_code, site_id=None):
"""
Returns language code while making sure it's in LANGUAGES
"""
if not language_code:
return None
site_id = _ensure_site_id(site_id, "get_language_code")
languages = get_language_list(site_id)
if language_code in languages: # direct hit
return language_code
for lang in languages:
if language_code.split('-')[0] == lang: # base language hit
return lang
if lang.split('-')[0] == language_code: # base language hit
return lang
return language_code
def get_current_language():
"""
Returns the currently active language
It's a replacement for Django's translation.get_language() to make sure the
LANGUAGE_CODE will be found in LANGUAGES.
Overcomes this issue: https://code.djangoproject.com/ticket/9340
"""
site_id = getattr(settings, 'SITE_ID', None)
language_code = translation.get_language()
if site_id:
return get_language_code(language_code, site_id=site_id)
# We do not know the site, return an entry from settings.LANGUAGES
languages = dict(getattr(settings, 'LANGUAGES', ((language_code, language_code), ))).keys()
if language_code in languages:
return language_code # direct hit
for lang in languages:
if language_code.split('-')[0] == lang: # base language hit
return lang
if lang.split('-')[0] == language_code: # base language hit
return lang
return language_code
def get_language_list(site_id=None):
"""
:return: returns a list of iso2codes for this site
"""
site_id = _ensure_site_id(site_id, "get_language_list")
return ([lang['code'] for lang in get_languages(site_id)] if settings.USE_I18N
else [settings.LANGUAGE_CODE])
def get_language_tuple(site_id=None):
"""
:return: returns an list of tuples like the old CMS_LANGUAGES or the LANGUAGES for this site
"""
site_id = _ensure_site_id(site_id, "get_language_tuple")
return [(lang['code'], lang['name']) for lang in get_languages(site_id)]
def get_language_dict(site_id=None):
"""
:return: returns an dict of cms languages
"""
site_id = _ensure_site_id(site_id, "get_language_dict")
return dict(get_language_tuple(site_id))
def get_public_languages(site_id=None):
"""
:return: list of iso2codes of public languages for this site
"""
site_id = _ensure_site_id(site_id, "get_public_languages")
return [lang['code'] for lang in get_language_objects(site_id)
if lang.get('public', True)]
def get_language_object(language_code, site_id=None):
"""
:param language_code: RFC5646 language code
:return: the language object filled up by defaults
"""
site_id = _ensure_site_id(site_id, "get_language_object")
for language in get_languages(site_id):
if language['code'] == get_language_code(language_code, site_id):
return language
raise LanguageError('Language not found: %s' % language_code)
def get_language_objects(site_id=None):
"""
returns list of all language objects filled up by default values
"""
site_id = _ensure_site_id(site_id, "get_language_objects")
return list(get_languages(site_id))
def get_default_language(language_code=None, site_id=None):
"""
Returns default language depending on settings.LANGUAGE_CODE merged with
best match from get_cms_setting('LANGUAGES')
Returns: language_code
"""
site_id = _ensure_site_id(site_id, "get_default_language")
if not language_code:
language_code = get_language_code(settings.LANGUAGE_CODE, site_id=site_id)
languages = get_language_list(site_id)
# first try if there is an exact language
if language_code in languages:
return language_code
# otherwise split the language code if possible, so iso3
language_code = language_code.split("-")[0]
if language_code not in languages:
return settings.LANGUAGE_CODE
return language_code
def get_default_language_for_site(site_id):
return get_language_list(site_id)[0]
def get_fallback_languages(language, site_id=None):
"""
returns a list of fallback languages for the given language
"""
site_id = _ensure_site_id(site_id, "get_fallback_languages")
try:
language = get_language_object(language, site_id)
except LanguageError:
language = get_languages(site_id)[0]
return language.get('fallbacks', [])
def get_redirect_on_fallback(language, site_id=None):
"""
returns if you should redirect on language fallback
:param language:
:param site_id:
:return: Boolean
"""
site_id = _ensure_site_id(site_id, "get_redirect_on_fallback")
language = get_language_object(language, site_id)
return language.get('redirect_on_fallback', True)
def hide_untranslated(language, site_id=None):
"""
Should untranslated pages in this language be hidden?
:param language:
:param site_id:
:return: A Boolean
"""
site_id = _ensure_site_id(site_id, "hide_untranslated")
obj = get_language_object(language, site_id)
return obj.get('hide_untranslated', True)
def is_language_prefix_patterns_used():
"""
Returns `True` if the `LocaleRegexURLResolver` or `LocalePrefixPattern`
is used at root level of the urlpatterns and doesn't have empty
language_prefix, else it returns `False`.
"""
for url_pattern in get_resolver(None).url_patterns:
pattern = getattr(url_pattern, 'pattern', url_pattern)
if isinstance(pattern, LocalePrefixPattern):
if pattern.language_prefix != '':
return True
return False
def is_valid_site_language(language, site_id):
return language in get_language_list(site_id)