Internationalisation (django-parler)¶
Le framework intègre django-parler pour la traduction des modèles.
Les champs traduisibles sont stockés dans des tables *Translation
séparées, ce qui permet un support multi-langue sans modifier le schéma
principal.
Configuration¶
Module : updo.settings
INSTALLED_APPS = [
...
'parler',
...
]
LANGUAGES = [
('en', 'English'),
('fr', 'Français'),
('ar', 'العربية'),
]
PARLER_LANGUAGES = {
None: (
{'code': 'en'},
{'code': 'fr'},
{'code': 'ar'},
),
'default': {
'fallbacks': ['en'],
'hide_untranslated': False,
},
}
fallbacks: si une traduction n’existe pas dans la langue active, django-parler utilise l’anglais comme fallback.hide_untranslated:Falsesignifie que les objets sans traduction dans la langue courante restent visibles (avec le contenu fallback).
Modèles de base traduisibles¶
Module : src.module.tenant.infrastructure.base_models
Modèle abstrait pour les entités traduisibles dans le schéma public. Combine
TranslatableModelde django-parler avecTenantMeta.tenant_type = "shared"pour le routage du database router.Utilisé par les données de référence (Country, Currency, etc.) et les modèles globaux (Role, Department du Tenant Context, Policy).
from parler.models import TranslatedFields from src.module.tenant.infrastructure.base_models import TranslatableSharedModel class Country(TranslatableSharedModel, ERPBaseModel): code = models.CharField(max_length=2, unique=True) translations = TranslatedFields( name=models.CharField(max_length=100), )
- class src.module.tenant.infrastructure.base_models.TranslatableTenantSpecificModel(TranslatableModel)¶
Modèle abstrait pour les entités traduisibles dans le schéma tenant. Combine
TranslatableModelavecTenantMeta.tenant_type = "tenant_specific"et le même garde-fousave()queTenantSpecificModel(auto-population detenant_id, vérification du contexte tenant).from parler.models import TranslatedFields from src.module.tenant.infrastructure.base_models import TranslatableTenantSpecificModel class Product(TranslatableTenantSpecificModel, ERPBaseModel): sku = models.CharField(max_length=50, unique=True) translations = TranslatedFields( name=models.CharField(max_length=255), description=models.TextField(blank=True), )
Modèles traduits — Données de référence¶
Ces modèles héritent de TranslatableSharedModel et stockent leurs
champs traduisibles dans des tables *Translation distinctes.
Modèle |
Champs traduits |
Table de traduction |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Modèles traduits — Tenant Context¶
Modèle |
Champs traduits |
Table de traduction |
|---|---|---|
|
|
|
|
|
|
Note
Role et Department conservent un champ name non traduit
comme identifiant programmatique (utilisé dans les JSON de rôles,
les conditions ABAC, etc.). Le champ display_name est la version
traduisible destinée à l’affichage.
Utilisation dans le code¶
Créer un objet avec traduction¶
from src.share_kernel.models import Country
country = Country(code="FR", code_alpha3="FRA")
country.set_current_language("en")
country.name = "France"
country.set_current_language("fr")
country.name = "France"
country.set_current_language("ar")
country.name = "فرنسا"
country.save()
Ou avec create_translation() :
country = Country.objects.create(code="MA", code_alpha3="MAR")
country.create_translation("en", name="Morocco")
country.create_translation("fr", name="Maroc")
country.create_translation("ar", name="المغرب")
Lire une traduction¶
from django.utils.translation import activate
activate("fr")
country = Country.objects.language("fr").get(code="MA")
print(country.name) # "Maroc"
# Avec fallback sécurisé
name = country.safe_translation_getter("name", any_language=True)
Requêtes filtrées par langue¶
# Toutes les devises dont le nom contient "dollar" en anglais
currencies = Currency.objects.language("en").filter(
translations__name__icontains="dollar",
)
# Toutes les devises avec traductions pré-chargées
currencies = Currency.objects.prefetch_related("translations").all()
Sérialisation DRF¶
Pour exposer les traductions via l’API REST, django-parler fournit
TranslatedModelSerializer :
from parler_rest.serializers import TranslatableModelSerializer, TranslatedFieldsField
class CountrySerializer(TranslatableModelSerializer):
translations = TranslatedFieldsField(shared_model=Country)
class Meta:
model = Country
fields = ["id", "code", "code_alpha3", "translations", "is_active"]
Cela produit un JSON de la forme :
{
"id": "...",
"code": "MA",
"translations": {
"en": {"name": "Morocco"},
"fr": {"name": "Maroc"},
"ar": {"name": "المغرب"}
}
}
Architecture des tables¶
Chaque modèle traduisible crée une table *Translation qui contient :
id: clé primaire auto-incrémentéelanguage_code: code de langue (en,fr,ar)master_id: FK vers le modèle parentLes champs traduits (
name,display_name,description, etc.)Contrainte unique :
(language_code, master_id)
ref_countries ref_countries_translation
┌──────────┐ ┌──────────────────────┐
│ id (PK) │◄──────────────│ master_id (FK) │
│ code │ │ language_code │
│ ... │ │ name │
└──────────┘ └──────────────────────┘
Cette architecture permet d’ajouter de nouvelles langues sans modifier la table principale.