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
|
from django.db import models
from django.utils.translation import get_language, gettext_lazy as _
LANGUAGE_CHOICES = [
("en", _("English")),
("ja", _("Japanese")),
("es", _("Spanish")),
("fr", _("French")),
("de", _("German")),
("zh", _("Chinese")),
("ko", _("Korean")),
]
class Translation(models.Model):
"""Base abstract model for translations"""
language = models.CharField(max_length=2, choices=LANGUAGE_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class TranslatableMixin:
@classmethod
def translate_queryset(cls, queryset, language_code="en"):
processed_objects = set()
return [obj.translate(language_code, processed_objects) for obj in queryset]
def translate(self, language_code="en", processed_objects=None):
if processed_objects is None:
processed_objects = set()
instance_id = f"{self.__class__.__name__}_{self.pk}"
if instance_id in processed_objects:
return self
processed_objects.add(instance_id)
translation = self.get_translation(language_code)
if translation:
translated_fields = [
field.name
for field in translation._meta.get_fields()
if not field.is_relation
and field.name not in ["id", "language", "created_at", "updated_at"]
]
for field_name in translated_fields:
translated_value = getattr(translation, field_name, None)
if translated_value is not None:
setattr(self, field_name, translated_value)
self._translate_relations(language_code, processed_objects)
return self
def _translate_relations(self, language_code, processed_objects):
for field in self._meta.get_fields():
if not hasattr(field, "related_model") or not hasattr(
field.related_model, "translate"
):
continue
if field.one_to_many or field.many_to_many:
related_manager = getattr(self, field.name, None)
if related_manager and hasattr(related_manager, "all"):
for obj in related_manager.all():
obj.translate(language_code, processed_objects)
elif field.many_to_one or field.one_to_one:
related_obj = getattr(self, field.name, None)
if related_obj and hasattr(related_obj, "translate"):
related_obj.translate(language_code, processed_objects)
def get_translation(self, language_code):
try:
return self.translations.get(language=language_code)
except self.translations.model.DoesNotExist:
return None
|