Translates Django models using a registration approach without modifying original model classes.
—
Translation field types, descriptors, and factory functions for creating and managing multilingual model fields. Handles the dynamic creation of language-specific fields and their integration with Django's model system.
Factory functions for dynamically creating translation fields based on original Django field types.
def create_translation_field(model, field_name, lang, empty_value):
"""
Create a translation field for a specific language.
Parameters:
- model: Model class containing the original field
- field_name: Name of the original field to translate
- lang: Language code for this translation field
- empty_value: How to handle empty values ('', 'both', None, NONE)
Returns:
- TranslationField instance
Raises:
- ImproperlyConfigured: If field type is not supported
"""
def field_factory(baseclass):
"""
Create a translation field class based on Django field type.
Parameters:
- baseclass: Django field class to base translation field on
Returns:
- TranslationField subclass
"""Usage Example:
from modeltranslation.fields import create_translation_field
from myapp.models import Article
# Create French translation field for title
title_fr_field = create_translation_field(
model=Article,
field_name='title',
lang='fr',
empty_value=''
)Base class for all translation fields, providing common functionality and field proxying.
class TranslationField:
"""
Base class for translation fields that proxy to original fields.
Attributes:
- translated_field: Original field being translated
- language: Language code for this translation
- empty_value: Empty value handling strategy
"""
def __init__(self, translated_field, language, empty_value):
"""
Initialize translation field.
Parameters:
- translated_field: Original Django field
- language: Language code (e.g., 'en', 'fr')
- empty_value: Empty value handling
"""
def contribute_to_class(self, cls, name):
"""Add this field to the model class."""
def formfield(self, **kwargs):
"""Create form field for this translation field."""Descriptors that handle field access and provide language-aware field behavior.
class TranslationFieldDescriptor:
"""
Descriptor for accessing translation fields with language awareness.
Handles fallback logic and value resolution.
"""
def __init__(self, field):
"""
Initialize descriptor for translation field.
Parameters:
- field: TranslationField instance
"""
def __get__(self, instance, owner):
"""Get field value with language-aware fallback."""
def __set__(self, instance, value):
"""Set field value for current language."""
class TranslatedRelationIdDescriptor:
"""Descriptor for translated foreign key ID fields."""
class TranslatedManyToManyDescriptor:
"""Descriptor for translated many-to-many relationships."""Django field types that can be translated by modeltranslation.
SUPPORTED_FIELDS: tuple = (
# Text fields
fields.CharField,
fields.TextField,
fields.json.JSONField,
# Numeric fields
fields.IntegerField,
fields.FloatField,
fields.DecimalField,
# Boolean fields
fields.BooleanField,
fields.NullBooleanField,
# Date/time fields
fields.DateField,
fields.DateTimeField,
fields.TimeField,
# File fields
fields.files.FileField,
fields.files.ImageField,
# Network fields
fields.IPAddressField,
fields.GenericIPAddressField,
# Relationship fields
fields.related.ForeignKey,
fields.related.ManyToManyField,
)
"""Tuple of Django field types supported for translation."""Custom Field Support:
# In Django settings.py
MODELTRANSLATION_CUSTOM_FIELDS = ('MyCustomField', 'AnotherField')
# Now MyCustomField can be used in translation
@register(MyModel)
class MyModelTranslationOptions(TranslationOptions):
fields = ('my_custom_field',) # Works if MyCustomField is in CUSTOM_FIELDSSentinel class and strategies for handling empty and undefined translation values.
class NONE:
"""
Sentinel class for undefined translation values.
Used when fallback value is not yet known and needs computation.
"""Empty Value Strategies:
@register(Article)
class ArticleTranslationOptions(TranslationOptions):
fields = ('title', 'content', 'summary')
empty_values = {
'title': '', # Only empty string is considered empty
'content': 'both', # Both None and empty string are empty
'summary': None, # Only None is considered empty
}Automatic handling of field attributes across translation fields.
# Original field
class Article(models.Model):
title = models.CharField(max_length=255, help_text="Article title")
content = models.TextField(blank=True, null=True)
# After registration, translation fields inherit attributes:
# title_en = CharField(max_length=255, help_text="Article title (English)")
# title_fr = CharField(max_length=255, help_text="Article title (French)")
# content_en = TextField(blank=True, null=True)
# content_fr = TextField(blank=True, null=True)Special handling for foreign key and many-to-many field translations.
# For ForeignKey fields
class TranslatedRelationIdDescriptor:
"""
Handles translated foreign key relationships.
Manages the _id suffix and related object access.
"""
# For ManyToManyField fields
class TranslatedManyToManyDescriptor:
"""
Handles translated many-to-many relationships.
Creates intermediate models for each language.
"""Relationship Translation Example:
class Article(models.Model):
title = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag)
@register(Article)
class ArticleTranslationOptions(TranslationOptions):
fields = ('title', 'category', 'tags')
# Creates fields:
# title_en, title_fr, title_de
# category_en, category_fr, category_de (ForeignKey fields)
# tags_en, tags_fr, tags_de (ManyToMany fields with intermediate models)Translation field validation and constraint handling.
class CustomTranslationField(TranslationField):
def validate(self, value, model_instance):
"""Custom validation for translation field."""
super().validate(value, model_instance)
# Add custom validation logic
if value and len(value) < 5:
raise ValidationError("Translation must be at least 5 characters")
def clean(self, value, model_instance):
"""Clean and prepare field value."""
value = super().clean(value, model_instance)
# Add custom cleaning logic
if isinstance(value, str):
value = value.strip()
return valueProgrammatically create translation fields at runtime.
from modeltranslation.fields import create_translation_field
def add_translation_field(model, field_name, language):
"""Dynamically add translation field to model."""
trans_field = create_translation_field(
model=model,
field_name=field_name,
lang=language,
empty_value=''
)
localized_name = f"{field_name}_{language}"
trans_field.contribute_to_class(model, localized_name)
return trans_fieldCreate custom descriptors for specialized translation behavior.
class CustomTranslationDescriptor:
def __init__(self, field_name, language):
self.field_name = field_name
self.language = language
def __get__(self, instance, owner):
if instance is None:
return self
# Custom logic for getting translated value
return getattr(instance, f"{self.field_name}_{self.language}", None)
def __set__(self, instance, value):
# Custom logic for setting translated value
setattr(instance, f"{self.field_name}_{self.language}", value)Handle field migrations when adding or modifying translations.
# Migration support for adding translation fields
from django.db import migrations
from modeltranslation.fields import TranslationField
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='article',
name='title_fr',
field=TranslationField(original_field='title', language='fr'),
),
]Optimize field access and memory usage for translation fields.
class OptimizedTranslationDescriptor:
def __init__(self, field):
self.field = field
self._cache_key = f"_{field.name}_cache"
def __get__(self, instance, owner):
if instance is None:
return self
# Use caching to improve performance
cache_key = self._cache_key
if hasattr(instance, cache_key):
return getattr(instance, cache_key)
value = self._get_translated_value(instance)
setattr(instance, cache_key, value)
return valueInstall with Tessl CLI
npx tessl i tessl/pypi-django-modeltranslation