2020-11-25 12:00:11 +02:00

284 lines
7.7 KiB
Python

from collections.abc import Iterable
from typing import Optional
import deprecation
from django.conf import settings
from django.utils import translation
class LocalizedValue(dict):
"""Represents the value of a :see:LocalizedField."""
default_value = None
def __init__(self, keys: dict = None):
"""Initializes a new instance of :see:LocalizedValue.
Arguments:
keys:
The keys to initialize this value with. Every
key contains the value of this field in a
different language.
"""
super().__init__({})
self._interpret_value(keys)
def get(self, language: str = None, default: str = None) -> str:
"""Gets the underlying value in the specified or primary language.
Arguments:
language:
The language to get the value in.
Returns:
The value in the current language, or
the primary language in case no language
was specified.
"""
language = language or settings.LANGUAGE_CODE
value = super().get(language, default)
return value if value is not None else default
def set(self, language: str, value: str):
"""Sets the value in the specified language.
Arguments:
language:
The language to set the value in.
value:
The value to set.
"""
self[language] = value
self.__dict__.update(self)
return self
def deconstruct(self) -> dict:
"""Deconstructs this value into a primitive type.
Returns:
A dictionary with all the localized values
contained in this instance.
"""
path = "localized_fields.value.%s" % self.__class__.__name__
return path, [self.__dict__], {}
def _interpret_value(self, value):
"""Interprets a value passed in the constructor as a
:see:LocalizedValue.
If string:
Assumes it's the default language.
If dict:
Each key is a language and the value a string
in that language.
If list:
Recurse into to apply rules above.
Arguments:
value:
The value to interpret.
"""
for lang_code, _ in settings.LANGUAGES:
self.set(lang_code, self.default_value)
if callable(value):
value = value()
if isinstance(value, str):
self.set(settings.LANGUAGE_CODE, value)
elif isinstance(value, dict):
for lang_code, _ in settings.LANGUAGES:
lang_value = value.get(lang_code, self.default_value)
self.set(lang_code, lang_value)
elif isinstance(value, Iterable):
for val in value:
self._interpret_value(val)
def translate(self, language: Optional[str] = None) -> Optional[str]:
"""Gets the value in the specified language (or active language).
Arguments:
language:
The language to get the value in. If not specified,
the currently active language is used.
Returns:
The value in the specified (or active) language. If no value
is available in the specified language, the value is returned
in one of the fallback languages.
"""
target_language = (
language or translation.get_language() or settings.LANGUAGE_CODE
)
fallback_config = getattr(settings, "LOCALIZED_FIELDS_FALLBACKS", {})
target_languages = fallback_config.get(
target_language, [settings.LANGUAGE_CODE]
)
for lang_code in [target_language] + target_languages:
value = self.get(lang_code)
if value:
return value or None
return None
def __str__(self) -> str:
"""Gets the value in the current language or falls back to the next
language if there's no value in the current language."""
return self.translate() or ""
def __eq__(self, other):
"""Compares :paramref:self to :paramref:other for equality.
Returns:
True when :paramref:self is equal to :paramref:other.
And False when they are not.
"""
if not isinstance(other, type(self)):
if isinstance(other, str):
return self.__str__() == other
return False
for lang_code, _ in settings.LANGUAGES:
if self.get(lang_code) != other.get(lang_code):
return False
return True
def __ne__(self, other):
"""Compares :paramref:self to :paramerf:other for in-equality.
Returns:
True when :paramref:self is not equal to :paramref:other.
And False when they are.
"""
return not self.__eq__(other)
def __setattr__(self, language: str, value: str):
"""Sets the value for a language with the specified name.
Arguments:
language:
The language to set the value in.
value:
The value to set.
"""
self.set(language, value)
def __repr__(self): # pragma: no cover
"""Gets a textual representation of this object."""
return "%s<%s> 0x%s" % (
self.__class__.__name__,
self.__dict__,
id(self),
)
class LocalizedStringValue(LocalizedValue):
default_value = ""
class LocalizedFileValue(LocalizedValue):
def __getattr__(self, name: str):
"""Proxies access to attributes to attributes of LocalizedFile."""
value = self.get(translation.get_language())
if hasattr(value, name):
return getattr(value, name)
raise AttributeError(
"'{}' object has no attribute '{}'".format(
self.__class__.__name__, name
)
)
def __str__(self) -> str:
"""Returns string representation of value."""
return str(super().__str__())
@deprecation.deprecated(
deprecated_in="4.6",
removed_in="5.0",
current_version="4.6",
details="Use the translate() function instead.",
)
def localized(self):
"""Returns value for current language."""
return self.get(translation.get_language())
class LocalizedNumericValue(LocalizedValue):
def __int__(self):
"""Gets the value in the current language as an integer."""
value = self.translate()
if value is None:
return self.default_value
return int(value)
def __str__(self) -> str:
"""Returns string representation of value."""
value = self.translate()
return str(value) if value is not None else ""
def __float__(self):
"""Gets the value in the current language as a float."""
value = self.translate()
if value is None:
return self.default_value
return float(value)
class LocalizedIntegerValue(LocalizedNumericValue):
"""All values are integers."""
default_value = None
def translate(self):
"""Gets the value in the current language, or in the configured fallbck
language."""
value = super().translate()
if value is None or (isinstance(value, str) and value.strip() == ""):
return None
return int(value)
class LocalizedFloatValue(LocalizedNumericValue):
"""All values are floats."""
default_value = None
def translate(self):
"""Gets the value in the current language, or in the configured
fallback language."""
value = super().translate()
if value is None or (isinstance(value, str) and value.strip() == ""):
return None
return float(value)