Système de Traduction Multilingue
Documentation complète du système de traduction multilingue, couvrant la traduction de l'interface, du contenu en base de données, l'intégration avec Filament, le cache, les modules, et les helpers.
Guides disponibles
Vue d'ensemble
Traduction de l'Interface
Fichiers JSON pour les textes statiques (boutons, labels, messages d'erreur).
- • Fichiers
lang/*.json - • Support de
fretes - • Laravel natif
Traduction du Contenu
Stockage des traductions en JSON dans les colonnes de la base de données.
- • Package
spatie/laravel-translatable - • Format JSON dans les colonnes
- • Trait
ConditionallyTranslatable
Gestion dans Filament
Interface d'administration pour gérer les traductions avec sélecteur de langue.
- • Helper
TranslatableField - • Action
LocaleAction - • Compatible Filament v5
Architecture du système
Composants du système
1. Traduction de l'Interface
Utilise le système natif de Laravel avec des fichiers JSON pour les textes statiques.
- • Fichiers :
lang/fr.json,lang/es.json - • Utilisation :
{{ __('messages.welcome') }} - • Package : Laravel natif
2. Traduction du Contenu (BDD)
Stockage des traductions en JSON dans les colonnes de la base de données.
- • Package :
spatie/laravel-translatable: ^6.12 - • Format : JSON dans les colonnes
- • Trait :
ConditionallyTranslatable - • Configuration :
config/translatable.php
3. Gestion dans Filament
Solution personnalisée pour Filament v5 avec helpers et actions.
- • Helper :
App\Filament\Support\TranslatableField - • Action :
App\Filament\Support\LocaleAction - • Sélecteur de langue dans le header
4. Routing et Localisation
URLs localisées avec détection automatique de la langue.
- • Package :
mcamara/laravel-localization - • Middleware :
SetLocaleMiddleware - • URLs :
/fr/blog,/es/blog
5. Système de Cache
Cache des traductions JSON et BDD pour améliorer les performances.
- • Service :
TranslationCacheService - • Commande :
translate:clear-cache - • Invalidation automatique via Observer
- • Support Redis, Memcached, File
6. Traductions dans les Modules
Support pour charger des traductions depuis les modules spécifiques.
- • Structure :
app/Specifics/{Module}/lang/ - • Service Provider par module
- • Chargement conditionnel
- • Exemple : Module Blog
7. Helpers et Utilitaires
Helpers globaux et classe TranslationHelper pour faciliter l'utilisation.
- • Helpers globaux :
current_locale(),trans_model(), etc. - • Classe :
TranslationHelper - • Routes localisées :
localized_route() - • Gestion des locales
Configuration
1. Configuration globale
Active ou désactive tout le système de traduction.
Fichier : .env
# Activer toutes les traductions
TRANSLATIONS_ENABLED=true
# Désactiver toutes les traductions
TRANSLATIONS_ENABLED=false
2. Configuration par composant
Active ou désactive chaque composant indépendamment.
Fichier : .env
# Activer globalement
TRANSLATIONS_ENABLED=true
# Interface utilisateur (fichiers JSON)
TRANSLATIONS_INTERFACE_ENABLED=true
# Base de données (Spatie Translatable)
TRANSLATIONS_DATABASE_ENABLED=true
# Routing (URLs localisées)
TRANSLATIONS_ROUTING_ENABLED=true
# Filament (plugin admin)
TRANSLATIONS_FILAMENT_ENABLED=true
Cas 1 : Interface uniquement
TRANSLATIONS_ENABLED=true
TRANSLATIONS_INTERFACE_ENABLED=true
TRANSLATIONS_DATABASE_ENABLED=false
TRANSLATIONS_ROUTING_ENABLED=false
TRANSLATIONS_FILAMENT_ENABLED=false
→ Seuls les textes de l'interface sont traduits
Cas 2 : BDD sans routing
TRANSLATIONS_ENABLED=true
TRANSLATIONS_INTERFACE_ENABLED=true
TRANSLATIONS_DATABASE_ENABLED=true
TRANSLATIONS_ROUTING_ENABLED=false
TRANSLATIONS_FILAMENT_ENABLED=true
→ Contenu traduisible en BDD, mais URLs non localisées
3. Configuration par modèle
Active ou désactive la traduction pour chaque modèle spécifique.
Fichier : .env
# Activer pour des modèles spécifiques
TRANSLATIONS_MODEL_POST=true
TRANSLATIONS_MODEL_PRODUCT=true
TRANSLATIONS_MODEL_FEEDBACK=true
TRANSLATIONS_MODEL_POLL=true
TRANSLATIONS_MODEL_ROADMAPITEM=true
Traduction du contenu en base de données
Contenu du guide :
- • Vue d'ensemble et modèles traduisibles (8 modèles migrés)
- • Utilisation dans les modèles avec le trait ConditionallyTranslatable
- • Migrations et structure des colonnes JSON
- • Commande de migration des données existantes (
translate:migrate-data) - • Actions de migration pour chaque modèle
- • Factories et Seeders adaptés
- • Intégration avec Filament (TranslatableField, LocaleAction)
- • Tests complets (106 tests, 344 assertions)
- • Activation/désactivation par modèle
- • Bonnes pratiques et dépannage
Modèles traduisibles
Les modèles suivants ont été migrés vers le système de traduction :
Blog
- • Post : title, slug, excerpt, content
Shop
- • Product : name, slug, description
Pages
- • Page : title, slug, content
Shares
- • Category : name, slug, description
- • Tag : name, slug
FeedbackHub
- • Feedback : title, description
- • RoadmapItem : title, description
- • Poll : title
Commande de migration
Utilisez la commande translate:migrate-data pour migrer les données existantes vers le format JSON.
# Migrer tous les modèles
php artisan translate:migrate-data
# Migrer un modèle spécifique
php artisan translate:migrate-data --model=Post
# Mode dry-run (simulation)
php artisan translate:migrate-data --dry-run
Note : La commande génère un rapport détaillé avec les statistiques de migration (enregistrements traités, migrés, ignorés, erreurs).
Utilisation dans les modèles
Trait ConditionallyTranslatable
Utilisez le trait ConditionallyTranslatable au lieu de HasTranslations directement pour respecter le système d'activation/désactivation.
use App\Specifics\Shares\Models\Concerns\ConditionallyTranslatable;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use ConditionallyTranslatable;
protected function getTranslatableFields(): array
{
return ['title', 'excerpt', 'content', 'slug'];
}
}
Méthodes disponibles
Récupération des traductions
// Récupérer une traduction spécifique
$post->getTranslation('title', 'fr'); // "Titre en français"
$post->getTranslation('title', 'es'); // "Título en español"
// Récupérer selon la locale courante
$post->title; // Retourne selon app()->getLocale()
// Récupérer toutes les traductions
$post->getTranslations('title');
// ['fr' => 'Titre en français', 'es' => 'Título en español']
Sauvegarde des traductions
// Définir une traduction
$post->setTranslation('title', 'fr', 'Titre en français');
$post->setTranslation('title', 'es', 'Título en español');
$post->save();
// Définir plusieurs traductions
$post->setTranslations('title', [
'fr' => 'Titre en français',
'es' => 'Título en español',
]);
$post->save();
Vérification des traductions
// Vérifier si une traduction existe
$post->hasTranslation('title', 'fr'); // true/false
// Fallback automatique
$post->getTranslation('title', 'es', true);
// Utilise le fallback si 'es' n'existe pas
Utilisation dans Filament
Helper TranslatableField
Utilisez le helper TranslatableField pour créer des champs traduisibles dans vos formulaires Filament.
use App\Filament\Support\TranslatableField;
use Filament\Schemas\Schema;
class ProductForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
// Champ texte traduisible
...TranslatableField::makeTextInput('name', 'Nom',
fn ($field) => $field->required()->maxLength(255)
),
// Champ textarea traduisible
...TranslatableField::makeTextarea('description', 'Description',
fn ($field) => $field->required()->rows(5)
),
]);
}
}
Action LocaleAction
Ajoutez le sélecteur de langue dans le header de vos pages Filament.
use App\Filament\Support\LocaleAction;
use Filament\Resources\Pages\EditRecord;
class EditProduct extends EditRecord
{
public ?string $locale = null;
public function mount(int | string $record): void
{
parent::mount($record);
$this->locale = current_locale();
}
protected function getHeaderActions(): array
{
return array_merge(
LocaleAction::make() ?? [],
parent::getHeaderActions()
);
}
}
Gestion des données dans les pages Filament standard
Note : Cette section concerne les pages Filament standard (sans composants Livewire imbriqués). Pour les pages avec composants imbriqués (comme EditPost), voir la section "Architecture simplifiée pour composants Livewire imbriqués" ci-dessous.
Utilisez les méthodes mutateFormDataBeforeFill et mutateFormDataBeforeSave pour transformer les données.
protected function mutateFormDataBeforeFill(array $data): array
{
// Transformer JSON en champs séparés (title_fr, title_es)
$translatableFields = ['title', 'description'];
foreach ($translatableFields as $field) {
if (isset($data[$field]) && is_string($data[$field])) {
$translations = json_decode($data[$field], true);
foreach (available_locales() as $locale) {
$data["{$field}_{$locale}"] = $translations[$locale] ?? null;
}
unset($data[$field]);
}
}
return $data;
}
protected function mutateFormDataBeforeSave(array $data): array
{
// Supprimer les champs locaux (title_fr, title_es)
$translatableFields = ['title', 'description'];
foreach ($translatableFields as $field) {
foreach (available_locales() as $locale) {
unset($data["{$field}_{$locale}"]);
}
}
return $data;
}
protected function afterSave(): void
{
// Sauvegarder les traductions
$translatableFields = ['title', 'description'];
foreach ($translatableFields as $field) {
foreach (available_locales() as $locale) {
$value = $this->data["{$field}_{$locale}"] ?? null;
if ($value !== null) {
$this->record->setTranslation($field, $locale, $value);
}
}
}
$this->record->save();
$this->fillForm();
}
Prise en charge des langues dans Filament
Sélecteur de langue (LocaleAction)
Le système fournit un sélecteur de langue intégré qui s'affiche dans le header des pages Filament. Il permet de changer de langue directement depuis l'interface d'administration.
Fonctionnalités :
- • Badge affichant la langue actuelle avec drapeau
- • Menu déroulant pour changer de langue
- • Mise à jour automatique des formulaires lors du changement
- • Synchronisation avec la session et l'application
Comportement lors du changement de langue
Lorsqu'un utilisateur change de langue via LocaleAction :
- Mise à jour de la locale : La locale de l'application et de la session est mise à jour
- Événement Livewire : Pour les pages avec composants imbriqués (EditPost, CreatePost), l'événement
locale-changedest dispatché - Sauvegarde automatique : Le composant imbriqué sauvegarde les valeurs actuelles dans
$translationsavant le changement - Affichage nouvelle locale : Les champs traduisibles affichent les valeurs de la nouvelle locale depuis
$translations - Mise à jour du heading : Le titre de la page se met à jour avec la traduction correspondante
Exemple de flux :
// 1. Utilisateur modifie title en français → stocké dans $translations['fr']['title']
// 2. Utilisateur clique sur "Espagnol" dans LocaleAction
// 3. LocaleAction dispatch 'locale-changed' avec locale='es'
// 4. Composant Edit::onLocaleChanged('es') :
// - Sauvegarde $this->title dans $translations['fr']['title']
// - Change $this->locale = 'es'
// - Affiche $translations['es']['title'] dans $this->title
// 5. L'utilisateur voit maintenant le titre espagnol (vide ou depuis BDD)
// 6. À l'enregistrement, TOUTES les traductions sont sauvegardées
Système de fallback automatique
Le système utilise automatiquement un fallback intelligent pour les traductions manquantes via la fonction trans_model() :
- Locale demandée : Essaie d'abord la langue sélectionnée
- Langue par défaut : Si vide, utilise la langue par défaut (fr)
- Autres langues : Si toujours vide, essaie toutes les autres langues disponibles
- Null : Retourne
nulluniquement si aucune traduction n'existe
Exemple :
// Article avec seulement le titre français
$post->setTranslation('title', 'fr', 'Mon article');
// Ces appels retourneront tous "Mon article"
trans_model($post, 'title', 'fr'); // "Mon article"
trans_model($post, 'title', 'es'); // "Mon article" (fallback vers fr)
trans_model($post, 'title'); // "Mon article" (locale actuelle ou fallback)
Initialisation de la locale
Toujours initialiser la propriété $locale dans la méthode mount() :
public ?string $locale = null;
public function mount(int|string $record): void
{
parent::mount($record);
$this->locale = current_locale(); // Important !
}
Cela garantit que la locale est synchronisée avec l'application, que les champs traduisibles affichent la bonne langue au chargement, et que le sélecteur de langue affiche la bonne langue active.
Architecture simplifiée pour composants Livewire imbriqués
Pour les composants Livewire imbriqués (comme EditPost ou CreatePost), utilisez un système simplifié avec un array $translations pour stocker toutes les traductions :
use Livewire\Attributes\On;
use Livewire\Component;
class Edit extends Component
{
public Post $post;
public string $title = '';
public string $slug = '';
public ?string $locale = null;
/**
* Stocke les traductions pour chaque locale.
* Format: ['fr' => ['title' => '...', 'slug' => '...'], 'es' => [...]]
*/
public array $translations = [];
public function mount(Post $post, ?string $locale = null): void
{
$this->post = $post;
$this->locale = $locale ?? current_locale();
// Charger TOUTES les traductions depuis la BDD
$this->loadAllTranslations();
// Afficher les champs pour la locale courante
$this->displayCurrentLocale();
}
/**
* Charge TOUTES les traductions depuis la BDD.
*/
protected function loadAllTranslations(): void
{
$locales = available_locales();
$fields = ['title', 'slug', 'excerpt', 'content'];
foreach ($locales as $loc) {
$this->translations[$loc] = [];
foreach ($fields as $field) {
$this->translations[$loc][$field] =
$this->post->getTranslation($field, $loc, false) ?? '';
}
}
}
/**
* Affiche les valeurs de la locale courante dans les champs.
*/
protected function displayCurrentLocale(): void
{
$loc = $this->locale;
$this->title = $this->translations[$loc]['title'] ?? '';
$this->slug = $this->translations[$loc]['slug'] ?? '';
$this->excerpt = $this->translations[$loc]['excerpt'] ?? '';
$this->content = $this->translations[$loc]['content'] ?? '';
}
/**
* Sauvegarde les valeurs actuelles dans $translations pour la locale courante.
*/
protected function saveCurrentLocaleToTranslations(): void
{
$loc = $this->locale;
$this->translations[$loc]['title'] = $this->title;
$this->translations[$loc]['slug'] = $this->slug;
$this->translations[$loc]['excerpt'] = $this->excerpt;
$this->translations[$loc]['content'] = $this->content;
}
/**
* Appelé quand la locale change depuis le parent.
*/
#[On('locale-changed')]
public function onLocaleChanged(string $locale): void
{
// 1. Sauvegarder les valeurs actuelles dans $translations
$this->saveCurrentLocaleToTranslations();
// 2. Changer la locale
$this->locale = $locale;
app()->setLocale($locale);
session()->put('locale', $locale);
// 3. Afficher les valeurs de la nouvelle locale
$this->displayCurrentLocale();
}
/**
* Appelé quand un champ traduisible est modifié.
* Sauvegarde automatiquement dans $translations.
*/
public function updated($property): void
{
$translatableFields = ['title', 'slug', 'excerpt', 'content'];
if (in_array($property, $translatableFields)) {
$this->translations[$this->locale][$property] = $this->{$property};
}
}
public function save(): void
{
// Sauvegarder les valeurs actuelles avant l'enregistrement
$this->saveCurrentLocaleToTranslations();
// Exécuter l'action de mise à jour avec les données de la locale courante
// ... (votre logique de sauvegarde)
// Sauvegarder TOUTES les traductions dans la BDD
$this->saveAllTranslations();
}
/**
* Sauvegarde TOUTES les traductions dans la BDD.
*/
protected function saveAllTranslations(): void
{
$fields = ['title', 'slug', 'excerpt', 'content'];
foreach ($this->translations as $locale => $data) {
foreach ($fields as $field) {
$value = $data[$field] ?? null;
if ($value !== null && $value !== '') {
$this->post->setTranslation($field, $locale, $value);
}
}
}
$this->post->save();
}
}
Avantages de cette architecture :
- • Simplicité : Pas de communication asynchrone entre composants
- • Isolation : Chaque locale a son propre espace dans
$translations - • Fiabilité : Pas de copie accidentelle entre locales
- • Performance : Chargement unique de toutes les traductions au mount
- • Maintenabilité : Code beaucoup plus simple et lisible
Flux utilisateur : L'utilisateur modifie un champ → stocké dans $translations[$locale][$field] → changement de locale → affichage de $translations[$newLocale][$field] → enregistrement → toutes les traductions sont sauvegardées.
Traduction de l'interface
Fichiers JSON
Les traductions de l'interface sont stockées dans des fichiers JSON.
Fichier : lang/fr.json
{
"Welcome": "Bienvenue",
"Login": "Connexion",
"Register": "Inscription"
}
Fichier : lang/es.json
{
"Welcome": "Bienvenido",
"Login": "Iniciar sesión",
"Register": "Registrarse"
}
Utilisation : {{ __('Welcome') }} retournera "Bienvenue" si la locale est 'fr', ou "Bienvenido" si la locale est 'es'.
Routing et localisation
URLs localisées
Le système génère automatiquement des URLs localisées pour chaque route avec préfixes de langue.
// URLs générées automatiquement
/fr/blog → Blog en français
/es/blog → Blog en español
/fr/blog/mon-article → Article en français
/es/blog/mi-articulo → Article en español
/fr/shop/products → Produits en français
/es/shop/products → Productos en español
Helper : Utilisez localized_route('blog.index') pour générer automatiquement l'URL avec le préfixe de langue. Si le routing est désactivé, la route est générée sans préfixe.
Slugs traduits dans les routes
Les routes avec paramètres de modèle (Post, Product, Page) utilisent des slugs traduits pour une meilleure SEO et une expérience utilisateur optimale.
// Exemple : Un article de blog avec slugs traduits
Post::create([
'slug' => [
'fr' => 'mon-article',
'es' => 'mi-articulo'
]
]);
// URLs générées
/fr/blog/mon-article → Article en français
/es/blog/mi-articulo → Article en español
// Si on accède à /fr/blog/mi-articulo
// → Redirection automatique vers /es/blog/mi-articulo
Queries pour récupérer les modèles par slug traduit
Des Queries dédiées sont utilisées pour résoudre les modèles par leur slug traduit :
- •
GetPostByTranslatedSlugQuery- Pour les articles de blog - •
GetProductByTranslatedSlugQuery- Pour les produits - •
GetPageByTranslatedSlugQuery- Pour les pages
Gestion du cas TRANSLATIONS_ENABLED=false : Les Queries cherchent d'abord dans les colonnes JSON (pour la locale par défaut puis les autres locales) même si les traductions sont désactivées. Cela permet de migrer progressivement d'un site traduit à un site non traduit.
Service de traduction de slugs
Le service SlugTranslationService centralise la logique de traduction des slugs lors du changement de langue.
use App\Services\SlugTranslationService;
$service = app(SlugTranslationService::class);
// Traduire les paramètres de route lors d'un changement de langue
$translatedParams = $service->translateSlugParams(
baseRouteName: 'blog.show',
routeParameters: ['slug' => 'mon-article'],
currentLocale: 'fr',
targetLocale: 'es'
);
// Retourne : ['slug' => 'mi-articulo']
Utilisation : Ce service est utilisé automatiquement par le contrôleur SwitchLocaleController et le composant Livewire LanguageSelector lors du changement de langue.
Composant de sélection de langue
Le composant Livewire LanguageSelector permet aux utilisateurs de changer de langue tout en conservant la page actuelle (avec traduction automatique des slugs).
// Dans une vue Blade
<livewire:language-selector />
// Le composant :
// - Affiche les langues disponibles
// - Permet de changer de langue
// - Redirige vers la même page dans la nouvelle langue
// - Traduit automatiquement les slugs si nécessaire
// - Stocke la préférence en session
Fonctionnement
- L'utilisateur clique sur une langue dans le sélecteur
- Le composant identifie la route actuelle et ses paramètres
- Si la route contient un slug (blog.show, shop.products.show, pages.show), le slug est traduit via
SlugTranslationService - La nouvelle URL est générée avec la locale cible et le slug traduit
- L'utilisateur est redirigé vers la nouvelle URL
Redirections inter-langues
Si un slug est trouvé dans une autre langue que la locale courante, une redirection automatique est effectuée vers la bonne URL.
// Exemple : Un article avec slug FR "mon-article" et ES "mi-articulo"
// Accès à /fr/blog/mi-articulo
// → Le système détecte que "mi-articulo" est le slug ES
// → Redirection automatique vers /es/blog/mi-articulo
// Accès à /es/blog/mon-article
// → Le système détecte que "mon-article" est le slug FR
// → Redirection automatique vers /fr/blog/mon-article
Note : Les redirections inter-langues sont conditionnelles sur translation_component_enabled('routing'). Si le routing est désactivé, aucune redirection n'est effectuée.
Système de cache des traductions
Contenu du guide :
- • Configuration du cache (TTL, store, activation)
- • Cache des traductions JSON (interface)
- • Cache des traductions BDD (modèles)
- • Invalidation automatique via Observer
- • Commande
translate:clear-cache - • Support des drivers (Redis, Memcached, File)
- • Tests de performance
- • Bonnes pratiques
Configuration
Le système de cache est configuré dans config/translations.php :
'cache' => [
'enabled' => env('TRANSLATIONS_CACHE_ENABLED', true),
'ttl' => env('TRANSLATIONS_CACHE_TTL', 86400), // 24 heures
'store' => env('TRANSLATIONS_CACHE_STORE', null), // null = cache par défaut
],
Utilisation
Le cache est automatiquement utilisé lors de la récupération des traductions :
// Cache automatique pour les traductions JSON
$translations = app(\App\Services\TranslationCacheService::class)
->getJsonTranslations('fr');
// Cache automatique pour les traductions BDD (via ConditionallyTranslatable)
$post->title; // Utilise le cache si activé
// Invalidation manuelle
app(\App\Services\TranslationCacheService::class)
->invalidateModelCache(Post::class, $post->id);
// Vider tout le cache
php artisan translate:clear-cache
Invalidation automatique
Le cache est automatiquement invalidé lors de la modification des modèles traduisibles grâce à l'Observer TranslatableModelObserver :
- • Création d'un modèle → Invalidation du cache
- • Mise à jour d'un modèle → Invalidation du cache
- • Suppression d'un modèle → Invalidation du cache
Note : L'Observer est automatiquement enregistré pour tous les modèles utilisant le trait ConditionallyTranslatable.
Traductions dans les modules
Contenu du guide :
- • Structure des fichiers de traduction dans les modules
- • Création d'un Service Provider par module
- • Chargement conditionnel des traductions
- • Exemple complet avec le module Blog
- • Tests et bonnes pratiques
Structure
Chaque module peut avoir ses propres fichiers de traduction dans un dossier lang/ :
app/Specifics/Blog/
├── lang/
│ ├── fr.json
│ └── es.json
├── Providers/
│ └── BlogServiceProvider.php
└── ...
Service Provider
Créez un Service Provider dans chaque module pour charger les traductions :
namespace App\Specifics\Blog\Providers;
use Illuminate\Support\ServiceProvider;
class BlogServiceProvider extends ServiceProvider
{
public function boot(): void
{
if (translation_component_enabled('interface')) {
$langPath = __DIR__.'/../lang';
if (is_dir($langPath)) {
$this->loadJsonTranslationsFrom($langPath);
}
}
}
}
Important : Enregistrez le Service Provider dans bootstrap/providers.php.
Format des fichiers JSON
Utilisez une structure plate avec notation pointée :
// lang/fr.json
{
"blog.title": "Blog",
"blog.posts": "Articles",
"blog.create_post": "Créer un article"
}
// Utilisation
__('blog.title'); // "Blog"
Helpers disponibles
Vérification de l'état
- •
translations_enabled()- Vérifie si les traductions sont activées - •
translation_component_enabled('database')- Vérifie un composant - •
translation_model_enabled('post')- Vérifie un modèle
Gestion des locales
- •
current_locale()- Locale courante - •
available_locales()- Locales disponibles - •
locale_name('fr')- Nom de la locale - •
locale_flag('fr')- Drapeau de la locale
Routing
- •
localized_route('blog.index')- Route localisée - •
localized_route('blog.show', ['slug' => $slug])- Route avec paramètres - •
route_is_localized('home')- Vérifie si la route actuelle correspond
Modèles
- •
trans_model($post, 'title')- Traduction d'un modèle - •
trans_model($post, 'slug')- Slug traduit d'un modèle
Classe TranslationHelper
Tous les helpers sont également disponibles via la classe TranslationHelper :
use App\Support\TranslationHelper;
// Locales
$locale = TranslationHelper::currentLocale();
$locales = TranslationHelper::availableLocales();
$name = TranslationHelper::localeName('fr');
// Routes
$url = TranslationHelper::localizedRoute('blog.index');
// Modèles
$title = TranslationHelper::transModel($post, 'title');
// Vérifications
$enabled = TranslationHelper::enabled();
$componentEnabled = TranslationHelper::componentEnabled('database');
$modelEnabled = TranslationHelper::modelEnabled('post');
Note : Consultez le Guide complet des helpers pour tous les détails et exemples.
Exemples pratiques
Exemple 1 : Créer un modèle traduisible
use App\Specifics\Shares\Models\Concerns\ConditionallyTranslatable;
class Product extends Model
{
use ConditionallyTranslatable;
protected function getTranslatableFields(): array
{
return ['name', 'description', 'slug'];
}
}
// Création avec traductions
$product = new Product();
$product->setTranslation('name', 'fr', 'Produit en français');
$product->setTranslation('name', 'es', 'Producto en español');
$product->save();
Exemple 2 : Afficher selon la locale
// Dans une vue Blade
<h1>{{ $product->name }}</h1>
// Affiche automatiquement selon app()->getLocale()
// Ou explicitement
<h1>{{ $product->getTranslation('name', 'es') }}</h1>
// Affiche toujours en espagnol
Exemple 3 : Migration de données existantes
// Convertir une colonne string en JSON
Schema::table('posts', function (Blueprint $table) {
$table->json('title')->nullable()->change();
});
// Migrer les données existantes
$posts = Post::all();
foreach ($posts as $post) {
$oldTitle = $post->title; // Ancienne valeur (string)
$post->setTranslation('title', 'fr', $oldTitle);
$post->save();
}
Exemple 4 : Utiliser les slugs traduits dans les vues
@foreach($posts as $post)
<a href="{{ localized_route('blog.show', ['slug' => trans_model($post, 'slug')]) }}">
{{ trans_model($post, 'title') }}
</a>
@endforeach
<a href="{{ localized_route('shop.products.show', ['slug' => trans_model($product, 'slug')]) }}">
{{ trans_model($product, 'name') }}
</a>
Important : Toujours utiliser localized_route() au lieu de route() pour les routes localisées. Cela garantit que les URLs sont correctement générées avec ou sans routing activé.
Exemple 5 : Vérifier la route actuelle avec localisation
Le helper route_is_localized() permet de vérifier si la route actuelle correspond à une route donnée, en tenant compte automatiquement de la localisation.
<x-application-logo class="{{ route_is_localized('home') ? 'active' : '' }}" />
@if(route_is_localized(['home', 'blog.index']))
<div class="active-menu">Menu actif</div>
@endif
@if(route_is_localized('blog.*'))
<div class="blog-section">Section blog</div>
@endif
<nav>
<a href="{{ localized_route('home') }}"
class="{{ route_is_localized('home') ? 'active' : '' }}">
Accueil
</a>
<a href="{{ localized_route('blog.index') }}"
class="{{ route_is_localized('blog.*') ? 'active' : '' }}">
Blog
</a>
</nav>
Note : Cette fonction fonctionne automatiquement avec ou sans localisation activée.
Si le routing est activé, elle teste toutes les variantes (fr.home, es.home, etc.).
Sinon, elle utilise routeIs() normal.
FAQ - Questions fréquentes
Pour une FAQ complète avec toutes les questions et réponses détaillées, consultez le Guide FAQ.
Comment activer/désactiver les traductions ?
Utilisez la variable TRANSLATIONS_ENABLED dans .env :
TRANSLATIONS_ENABLED=true # Activer
TRANSLATIONS_ENABLED=false # Désactiver
Puis-je activer les traductions pour seulement certains modèles ?
Oui, utilisez les variables TRANSLATIONS_MODEL_* pour chaque modèle.
Comment rendre un modèle traduisible ?
Consultez le Guide pour rendre un modèle traduisible.
Comment ajouter une nouvelle langue ?
Consultez le Guide pour ajouter une nouvelle langue.
Comment vider le cache des traductions ?
# Vider tout le cache
php artisan translate:clear-cache
# Vider uniquement le cache JSON
php artisan translate:clear-cache --json
# Vider uniquement le cache BDD
php artisan translate:clear-cache --db
# Vider pour une locale spécifique
php artisan translate:clear-cache --locale=fr
Comment utiliser les traductions dans les modules ?
Consultez le Guide des traductions dans les modules.
Quels tests sont disponibles ?
Le système dispose de 204+ tests avec une couverture estimée de > 88%. Le rapport de couverture complet est disponible dans docs/testing/translation-test-coverage-report.md.
Dépannage
Problème : Les traductions ne sont pas sauvegardées
- • Vérifier que
TRANSLATIONS_ENABLED=true - • Vérifier que
TRANSLATIONS_DATABASE_ENABLED=true - • Vérifier que
TRANSLATIONS_MODEL_{MODEL}=true - • Vérifier que la colonne est de type JSON
Problème : Le fallback ne fonctionne pas
- • Vérifier
use_fallback => truedansconfig/translatable.php - • Vérifier que
fallback_localeest correctement configuré
Problème : Les champs Filament ne s'affichent pas
- • Vérifier que
TRANSLATIONS_FILAMENT_ENABLED=true - • Vérifier que
LocaleAction::make()est ajouté dansgetHeaderActions() - • Vérifier que
$localeest initialisé dansmount()
Problème : RouteNotFoundException lors du changement de langue
- • Vérifier que vous utilisez
localized_route()et nonroute()pour les routes localisées - • Vérifier que la route existe avec le préfixe de locale (ex:
fr.appointments.index) - • Vérifier que
TRANSLATIONS_ROUTING_ENABLED=truesi vous utilisez des routes localisées
Problème : 404 sur les pages avec slugs traduits
- • Vérifier que les Queries (
GetPostByTranslatedSlugQuery, etc.) sont correctement configurées - • Vérifier que les slugs sont bien stockés en JSON dans la base de données
- • Si
TRANSLATIONS_ENABLED=false, vérifier que les Queries cherchent aussi dans les colonnes JSON (fonctionnalité déjà implémentée) - • Vérifier que le trait
ConditionallyTranslatableest utilisé sur le modèle
Problème : TypeError avec htmlspecialchars() lors de TRANSLATIONS_ENABLED=false
- • Vérifier que le trait
ConditionallyTranslatableest utilisé sur le modèle - • Le trait ajuste automatiquement les casts (array si traductions activées, string sinon)
- • Les accesseurs extraient automatiquement la valeur de la locale par défaut depuis les colonnes JSON
Références
Documentation officielle
Guides du projet
Fichiers du projet
- •
config/translatable.php - •
config/translations.php - •
bootstrap/helpers.php- Helpers globaux - •
app/Support/TranslationHelper.php- Classe helper - •
app/Services/TranslationCacheService.php- Service de cache - •
app/Console/Commands/TranslateClearCacheCommand.php- Commande de cache - •
app/Observers/TranslatableModelObserver.php- Observer pour invalidation - •
app/Services/TranslationMigrationService.php- Service de migration - •
app/Console/Commands/TranslateMigrateDataCommand.php- Commande de migration - •
app/Filament/Support/TranslatableField.php- Helper Filament - •
app/Filament/Support/LocaleAction.php- Action Filament - •
app/Specifics/Shares/Models/Concerns/ConditionallyTranslatable.php- Trait - •
app/Specifics/{Module}/Providers/{Module}ServiceProvider.php- Service Providers modules - •
tests/Feature/TranslationRegressionTest.php- Tests de régression - •
docs/testing/translation-test-coverage-report.md- Rapport de couverture