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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
from django.conf import settings
from django.db import models
from django.utils import timezone
LANGUAGE_CHOICES = [
("en", "English"),
("ja", "Japanese"),
("es", "Spanish"),
("fr", "French"),
("de", "German"),
("zh", "Chinese"),
("ko", "Korean"),
]
class Translation(models.Model):
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 AnnouncementTranslation(Translation):
announcement = models.ForeignKey(
"Announcement", on_delete=models.CASCADE, related_name="translations"
)
content = models.TextField()
class Meta:
unique_together = ("announcement", "language")
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
class Announcement(TranslatableMixin, models.Model):
content = models.TextField()
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
is_public = models.BooleanField(default=False)
is_new = models.BooleanField(default=True)
created_at = models.DateTimeField()
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.content[:50] + "..." if len(self.content) > 50 else self.content
def save(self, *args, **kwargs):
if not self.created_at:
self.created_at = timezone.now()
return super().save(*args, **kwargs)
|