Files
Holger Sielaff 79c68169f6 Initial
2025-08-02 20:08:33 +02:00

178 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
"""
Django Translatable Fields - Context Management
This module provides context managers and utilities for temporarily overriding
language context when working with translatable fields. It allows you to
bypass Django's current language setting and force specific language contexts
for translatable field operations.
Key Features:
- Thread-safe language context storage
- Context managers for temporary language overrides
- Integration with Django's language activation system
- Support for nested context operations
- Automatic cleanup and restoration
Context Hierarchy (highest to lowest priority):
1. Explicit language parameter in method calls
2. QuerySet context (with_context)
3. Global context manager (translatable_context)
4. Django's current language (get_language)
5. Default fallback ('en_US')
Usage:
# Global context for code blocks
with translatable_context('de'):
products = Product.objects.search('test') # Uses German
# Django language context
with django_language_context('fr'):
# Django's get_language() returns 'fr'
pass
Author: Holger Sielaff <holger@backender.de>
Version: 0.1.0
"""
from contextlib import contextmanager
from django.utils.translation import get_language, activate, deactivate
import threading
# Thread-local storage for context overrides
_context_storage = threading.local()
class TranslatableContext:
"""
Context manager for temporarily setting language context for translatable operations
"""
def __init__(self, language):
self.language = language
self.previous_language = None
def __enter__(self):
self.previous_language = getattr(_context_storage, 'language', None)
_context_storage.language = self.language
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.previous_language is not None:
_context_storage.language = self.previous_language
else:
if hasattr(_context_storage, 'language'):
delattr(_context_storage, 'language')
return False
@contextmanager
def translatable_context(language):
"""
Context manager for temporarily overriding language context.
Args:
language: Language code to use
Example:
with translatable_context('de'):
products = Product.objects.search('moep') # Searches in German
for product in products:
print(product.name) # Shows German name
"""
with TranslatableContext(language):
yield
@contextmanager
def django_language_context(language):
"""
Context manager that temporarily changes Django's active language.
Args:
language: Language code to activate
Example:
with django_language_context('de'):
# Django's get_language() will return 'de'
products = Product.objects.search('moep')
"""
previous_language = get_language()
try:
activate(language)
yield
finally:
if previous_language:
activate(previous_language)
else:
deactivate()
def get_context_language():
"""
Get the current context language override, if any.
Returns:
Language code from context, or None if no override
"""
return getattr(_context_storage, 'language', None)
def get_effective_language(explicit_language=None):
"""
Get the effective language to use for translatable operations.
Priority:
1. Explicit language parameter
2. Context language override
3. Django's current language
4. Default fallback (en_US)
Args:
explicit_language: Explicitly specified language
Returns:
Language code to use
"""
if explicit_language is not None:
return explicit_language
context_lang = get_context_language()
if context_lang is not None:
return context_lang
django_lang = get_language()
if django_lang:
return django_lang
return 'en_US'
class LanguageContextMixin:
"""
Mixin that adds context language awareness to QuerySets and Managers
"""
def _get_effective_language(self, language=None):
"""Get effective language considering all sources"""
return get_effective_language(language)
# Decorator for functions that should respect language context
def with_language_context(func):
"""
Decorator that makes a function respect the current language context.
Example:
@with_language_context
def get_product_name(product):
return product.name # Will use context language if set
"""
def wrapper(*args, **kwargs):
# If 'language' not explicitly provided, inject context language
if 'language' not in kwargs:
context_lang = get_context_language()
if context_lang:
kwargs['language'] = context_lang
return func(*args, **kwargs)
return wrapper