Translates Django models using a registration approach without modifying original model classes.
—
Multilingual managers and querysets that automatically handle language-specific database queries, field lookups, and provide fallback functionality for missing translations.
Custom manager that automatically rewrites queries to use appropriate translation fields based on the current language.
class MultilingualManager(models.Manager):
"""
Manager for multilingual models that handles language-aware queries.
Automatically:
- Rewrites field lookups to use current language fields
- Handles fallback languages for queries
- Manages related model query rewriting
"""
def get_queryset(self):
"""Return queryset with translation field rewriting."""
def filter(self, *args, **kwargs):
"""Filter with automatic translation field rewriting."""
def exclude(self, *args, **kwargs):
"""Exclude with automatic translation field rewriting."""
def order_by(self, *field_names):
"""Order by with translation field rewriting."""Usage Example:
from modeltranslation.manager import MultilingualManager
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
objects = MultilingualManager()
# Queries automatically use current language
articles = Article.objects.filter(title__icontains='django')
# Becomes: filter(title_en__icontains='django') if current language is 'en'
articles = Article.objects.order_by('title')
# Becomes: order_by('title_en') if current language is 'en'QuerySet that provides language-aware query operations and fallback handling.
class MultilingualQuerySet(models.QuerySet):
"""
QuerySet with automatic translation field handling.
Features:
- Language-aware field lookups
- Fallback language support
- Translation field aggregation
"""
def filter(self, *args, **kwargs):
"""Filter with translation field rewriting."""
def exclude(self, *args, **kwargs):
"""Exclude with translation field rewriting."""
def order_by(self, *field_names):
"""Order with translation field rewriting."""
def values(self, *fields):
"""Values with translation and fallback field inclusion."""
def values_list(self, *fields, **kwargs):
"""Values list with translation field handling."""Manager that provides both manager and queryset functionality in one class.
class MultilingualQuerysetManager(MultilingualManager):
"""
Combined manager/queryset providing both manager and queryset methods.
Enables method chaining while maintaining translation functionality.
"""
def get_queryset(self):
"""Return MultilingualQuerySet instance."""
# Inherits all QuerySet methods for chainingUsage Example:
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
published = models.BooleanField(default=False)
objects = MultilingualQuerysetManager.from_queryset(MultilingualQuerySet)()
# Method chaining with translation support
recent_articles = (Article.objects
.filter(published=True)
.filter(title__icontains='django')
.order_by('-created_at')[:10])Automatic rewriting of field lookups to use appropriate translation fields.
def rewrite_lookup_key(model, lookup_key):
"""
Rewrite field lookup keys to use translation fields.
Parameters:
- model: Model class being queried
- lookup_key: Original lookup key (e.g., 'title__icontains')
Returns:
- str: Rewritten lookup key (e.g., 'title_en__icontains')
"""
def get_translatable_fields_for_model(model):
"""
Get list of translatable field names for a model.
Parameters:
- model: Model class
Returns:
- list: Field names that are translatable, or None if not registered
"""Lookup Rewriting Examples:
# Simple field lookups
Article.objects.filter(title='Django')
# → filter(title_en='Django')
# Complex lookups with field transforms
Article.objects.filter(title__icontains='django', content__startswith='This')
# → filter(title_en__icontains='django', content_en__startswith='This')
# Related field lookups
Article.objects.filter(category__name='Technology')
# → filter(category__name_en='Technology') if Category.name is translatable
# Date/numeric fields work normally
Article.objects.filter(created_at__gte=date.today())
# → No rewriting needed for non-translatable fieldsAutomatic inclusion of fallback language fields in queries and values.
def append_fallback(model, fields):
"""
Add fallback language fields to field list.
Parameters:
- model: Model class
- fields: Sequence of field names
Returns:
- tuple: (expanded_fields_set, translated_field_names_set)
"""
def append_translated(model, fields):
"""
Add all translation fields for given field names.
Parameters:
- model: Model class
- fields: Iterable of field names
Returns:
- set: All translation field names for given fields
"""Fallback Usage:
# When querying values, fallback fields are automatically included
articles = Article.objects.values('title', 'content')
# If current language is 'fr' with fallback to 'en', this becomes:
# values('title_fr', 'title_en', 'content_fr', 'content_en')
# The model instance will show the French value if available,
# otherwise the English fallbackAggregation functions that work with translation fields and fallbacks.
from django.db.models import Count, Q
from modeltranslation.utils import get_language
# Count articles with non-empty titles in current language
current_lang = get_language()
Article.objects.aggregate(
count=Count('id', filter=Q(**{f'title_{current_lang}__isnull': False}))
)
# Aggregate across all languages
from modeltranslation.settings import AVAILABLE_LANGUAGES
for lang in AVAILABLE_LANGUAGES:
count = Article.objects.filter(**{f'title_{lang}__isnull': False}).count()
print(f"Articles with {lang} title: {count}")Extend querysets with translation-specific methods.
class ArticleQuerySet(MultilingualQuerySet):
def published(self):
"""Filter for published articles."""
return self.filter(published=True)
def with_translation(self, language=None):
"""Filter articles that have translation in specified language."""
from modeltranslation.utils import get_language
lang = language or get_language()
# Get translatable fields for this model
from modeltranslation.translator import translator
opts = translator.get_options_for_model(self.model)
# Create filter conditions for non-empty translation fields
conditions = Q()
for field_name in opts.fields:
field_lookup = f"{field_name}_{lang}__isnull"
conditions &= ~Q(**{field_lookup: True})
return self.filter(conditions)
def missing_translation(self, language=None):
"""Filter articles missing translation in specified language."""
from modeltranslation.utils import get_language
lang = language or get_language()
from modeltranslation.translator import translator
opts = translator.get_options_for_model(self.model)
conditions = Q()
for field_name in opts.fields:
field_lookup = f"{field_name}_{lang}__isnull"
conditions |= Q(**{field_lookup: True})
return self.filter(conditions)
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
published = models.BooleanField(default=False)
objects = MultilingualQuerysetManager.from_queryset(ArticleQuerySet)()
# Usage
published_with_french = Article.objects.published().with_translation('fr')
missing_german = Article.objects.missing_translation('de')Handle translation fields in related model queries and joins.
# Related model lookups are automatically rewritten
Article.objects.filter(category__name='Technology')
# If Category.name is translatable:
# → filter(category__name_en='Technology')
# Prefetch related with translations
Article.objects.prefetch_related('category').filter(title__icontains='django')
# Select related with translation fields
Article.objects.select_related('category').values('title', 'category__name')
# Automatically includes appropriate translation fieldsOverride field resolution for complex translation scenarios.
class CustomMultilingualQuerySet(MultilingualQuerySet):
def _rewrite_filter_args(self, *args, **kwargs):
"""Custom logic for rewriting filter arguments."""
# Custom field resolution logic
return super()._rewrite_filter_args(*args, **kwargs)Optimize queries for large datasets with translations.
# Use only() and defer() with translation fields
Article.objects.only('title_en', 'title_fr').filter(published=True)
# Defer translation fields not needed immediately
Article.objects.defer('content_de', 'content_es').filter(category__name='Tech')
# Use select_related for foreign key translations
Article.objects.select_related('category').filter(title__icontains='django')Handle raw SQL queries with translation field names.
from modeltranslation.utils import build_localized_fieldname, get_language
current_lang = get_language()
title_field = build_localized_fieldname('title', current_lang)
# Raw query with dynamic field names
Article.objects.extra(
where=[f"{title_field} ILIKE %s"],
params=['%django%']
)Bulk create, update, and delete operations with translation fields.
# Bulk create with translation fields
articles = [
Article(title_en='English Title', title_fr='Titre Français'),
Article(title_en='Another Title', title_fr='Autre Titre'),
]
Article.objects.bulk_create(articles)
# Bulk update translation fields
Article.objects.filter(category_id=1).update(
title_fr='Nouveau Titre',
content_fr='Nouveau contenu'
)Install with Tessl CLI
npx tessl i tessl/pypi-django-modeltranslation