178 lines
5.0 KiB
Python
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 |