# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
import babel
import babel.numbers
from babel import UnknownLocaleError
from babel.dates import format_datetime
from babel.numbers import format_currency, format_decimal, parse_pattern
from django.apps import apps
from django.utils import translation
from django.utils.timezone import localtime
from django.utils.translation import get_language
from django.views.decorators.cache import cache_page
from functools import lru_cache, wraps
[docs]def lang_lru_cache(func):
"""Language aware least recently used cache decorator"""
@lru_cache()
def cached(*args, __lang=None, **kwargs):
return func(*args, **kwargs)
@wraps(func)
def wrapper(*args, **kwargs):
return cached(*args, **kwargs, __lang=translation.get_language())
wrapper.cache_clear = cached.cache_clear
return wrapper
@lru_cache()
[docs]def get_babel_locale(locale_string):
"""
Parse a Django-format (dash-separated) locale string
and return a Babel locale.
This function is decorated with lru_cache, so executions
should be cheap even if `babel.Locale.parse()` most definitely
is not.
:param locale_string: A locale string ("en-US", "fi-FI", "fi")
:type locale_string: str
:return: Babel Locale
:rtype: babel.Locale
"""
return babel.Locale.parse(locale_string, "-")
[docs]def get_current_babel_locale(fallback="en-US-POSIX"):
"""
Get a Babel locale based on the thread's locale context.
:param fallback:
Locale to fallback to; set to None to raise an exception instead.
:return: Babel Locale
:rtype: babel.Locale
"""
locale = get_babel_locale(locale_string=translation.get_language())
if not locale:
if fallback:
locale = get_babel_locale(fallback)
if not locale:
raise ValueError("Error! Failed to get the current babel locale (lang=%s)." % (translation.get_language(),))
return locale
@lang_lru_cache
[docs]def get_language_name(language_code):
"""
Get a language's name in the currently active locale.
:param language_code: Language code (possibly with an added script suffix (zh_Hans, zh-Hans))
:type language_code: str
:return: The language name, or the code if the language couldn't be derived.
:rtype: str
"""
try:
lang_dict = get_current_babel_locale().languages
except (AttributeError, ValueError): # The locale lookup failed,
return language_code # so return the code as-is.
for option in (
language_code,
str(language_code).replace("-", "_"),
):
if option in lang_dict:
return lang_dict[option]
return language_code
@cache_page(3600, key_prefix="js18n-%s" % get_language())
[docs]def javascript_catalog_all(request, domain="djangojs"):
"""
Get JavaScript message catalog for all apps in `INSTALLED_APPS`.
"""
all_apps = [x.name for x in apps.get_app_configs()]
from django.views.i18n import JavaScriptCatalog
js_catalog = JavaScriptCatalog(packages=all_apps, domain=domain)
return js_catalog.get(request)
[docs]def get_currency_name(currency):
locale = get_current_babel_locale()
return babel.numbers.get_currency_name(currency, locale=locale)
[docs]def is_existing_language(language_code):
"""
Try to find out if the language actually exists.
Calling `babel.Locale("en").languages.keys()`
will contain extinct languages.
:param language_code: A language code string ("fi", "en")
:type language_code: str
:return: True or False
:rtype: bool
"""
try:
get_babel_locale(language_code)
except (UnknownLocaleError, ValueError):
"""
Catch errors with babel locale parsing.
For example language `bew` raises `UnknownLocaleError`
and `ValueError` is being raised if language_code is
an empty string.
"""
return False
return True
@lru_cache()
[docs]def remove_extinct_languages(language_codes):
language_codes = set(language_codes)
codes = language_codes.copy()
for language_code in codes:
if not is_existing_language(language_code):
language_codes.remove(language_code)
return language_codes