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