Symfony'de Farklı Route’lar İçin Varsayılan Translation Domain Nasıl Ayarlanır?
Yeni başladığım projemde, hem önyüzde hem de yönetim paneli tarafında birden fazla dil desteği vermeyi planladım ve Symfony 7 ile altyapımı kurdum. Symfony’de çeviri dosyaları, bildiğiniz gibi, /var/translations
klasörü içinde domain.locale.yaml
formatında bulunur. Siz aksini belirtmediğiniz sürece de çevirilecek metinlerin (eğer İngilizce çevirisi kullanacaksanız) messages.en.yaml
dosyasında olması gerekir. (messages
= varsayılan translation_domain
, en
= locale
).
Hem önyüz hem de yönetim panelindeki çevirilerin tek bir dosyada olması, ileride benim için yönetimi zorlaştıracağından dolayı önyüz için ayrı, panel için ayrı dosyalar kullanmaya karar verdim. İlk etapta önyüz için özel bir translation_domain
oluşturmak istemedim çünkü trans()
metodlarında her seferinde translation_domain
belirtmek istemiyordum. Bu sayede önyüzde messages.en.yaml
dosyasına ekleyeceğim çevirileri controller'da $this->translator->trans('cevrilecek_metin')
ve Twig'de 'cevrilecek_metin'|trans
şeklinde kullanabilecektim.
Yönetim paneli tarafında ise durum biraz daha karmaşıklaşıyordu. Panel için admin.en.yaml
dosyasını oluşturup çevirilerimi buraya ekleyince, controller tarafında $this->translator->trans('cevrilecek_metin', [], 'admin')
, Twig'de ise trans([], 'admin')
şeklinde kullanmak zorundaydım. Bu da haliyle hem yazarken zorlayacak, hem de düşük bir ihtimal de olsa translation_domain
in ismini değiştirmem gerektiğinde tüm bu kullanımları tek tek değiştirmem anlamına gelecekti.
İstediğim şey ise, tıpkı önyüzdeki gibi herhangi bir translation_domain
belirtmeden yazabilmekti. Bunun için sadece yönetim paneline özel bir translation domaini varsayılan olarak belirlemem gerekiyordu. Çeşitli kaynaklardan parça parça derlediğim bilgileri ChatGPT'nin de yardımıyla sadeleştirerek bir sonuca ulaştım.
Özetleyecek olursam, kurmak istediğim yapıyı aşağıdaki 5 adımla tamamladım:
- Custom bir translator servisi oluşturdum.
- Route’larımın request’lerine, o route boyunca kullanılacak
translation_domain
in adını bir varsayılan değer olarak enjekte ettim. - Bir kernel event listener’ı oluşturarak custom translator’un varsayılan domainini belirledim.
- BaseController’ıma CustomTranslator’u ekledim.
- Twig’in
trans
filtresini override ettim.
Şimdi bu adımları teker teker detaylandıralım.
1. Custom Translator Servisi Oluşturma
Öncelikle TranslatorInterface
'i temel alan bir sınıf hazırladım. Bu sınıf, eğer varsa bir translation_domain
i varsayılan olarak kaydetmekten başka bir iş yapmıyor.
// src/Service/CustomTranslator.php
namespace App\Service;
use Symfony\Contracts\Translation\TranslatorInterface;
class CustomTranslator implements TranslatorInterface
{
private $translator;
private $defaultDomain;
public function __construct(TranslatorInterface $translator, string $defaultDomain = 'messages')
{
$this->translator = $translator;
$this->defaultDomain = $defaultDomain;
}
public function setDefaultDomain(string $defaultDomain): void
{
$this->defaultDomain = $defaultDomain;
}
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
{
if ($domain === null) {
$domain = $this->defaultDomain;
}
return $this->translator->trans($id, $parameters, $domain, $locale);
}
public function getLocale(): string
{
return $this->translator->getLocale();
}
}
2. Request’lere Varsayılan Parametre Enjekte Edilmesi
Event listener’ı hazırlamadan önce, route’larımın request’lerine varsayılan bir parametre enjekte ederek bu parametrenin değerinin kullanacağım translation_domain
olmasını sağladım.
# config/routes.yaml
app:
resource: frontend_routes.yaml
prefix: /
defaults:
_translation_domain: 'frontend'
admin:
resource: cms_routes.yaml
prefix: 'cms'
defaults:
_translation_domain: 'admin'
Route’larımı YAML dosyalarında tutuyorum. Eğer siz annotation kullanıyorsanız, aşağıdaki gibi bir kullanım da işinizi görecektir:
// src/Controller/FrontendController.php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/cms', defaults: ['_translation_domain' => 'admin'])]
class AdminController extends BaseController
{
// ...
}
3. Event Listener ile CustomTranslator’a Varsayılan Domainin Bildirilmesi
Event listener’da, eklediğim request parametresini alarak CustomTranslator’umun varsayılan domaini olarak ayarlıyorum:
// src/Event/Listener/TranslationDomainListener.php
namespace App\Event\Listener;
use App\Service\CustomTranslator;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class TranslationDomainListener
{
private $translator;
public function __construct(CustomTranslator $translator)
{
$this->translator = $translator;
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$translationDomain = $request->attributes->get('_translation_domain');
if ($translationDomain) {
$this->translator->setDefaultDomain($translationDomain);
}
}
}
Son olarak services.yaml
dosyama aşağıdaki eklemeleri yaparak servisimi kaydediyorum:
# config/services.yaml
services:
App\Event\Listener\TranslationDomainListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Artık yönetim panelimde hangi sayfaya girersem gireyim, _translation_domain
parametresi varsayılan olarak request'lerim arasına ekleniyor. Bunu profiler
da gözlemleyebilirsiniz:
4. CustomTranslator’un Projede Kullanılması
Projelerimde her zaman bir BaseController
oluşturup alt sınıflarımı bu controller'ı extend ederek kullanırım. CustomTranslator'umu BaseController'ıma aşağıdaki gibi bağlayarak, controller metotlarımın içinde $this->translator
aracılığıyla hızlıca çevirilerimi yapabiliyorum.
// src/Controller/BaseController.php
namespace App\Controller;
use App\Service\CustomTranslator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class BaseController extends AbstractController
{
protected $translator;
public function __construct(CustomTranslator $translator)
{
$this->translator = $translator;
}
}
AdminController
'ımda artık translation_domain
belirtmeden, çevirilerimin /var/translation/admin.en.yaml
dosyasından okunmasını sağlamış oldum.
// src/Controller/AdminController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
class AdminController extends BaseController
{
public function index()
{
return new Response($this->translator->trans('Bu bir denemedir'));
}
}
5. Twig’de Çeviri Filtresinin Güncellenmesi
Twig’de kullanılan trans
filtresi, varsayılan TranslatorInterface
'i kullandığı için yukarıdaki yöntem burada işe yaramıyor. Bunu aşabilmek için bir Twig filtresi oluşturarak trans
filtresini override ettim ve aşağıdaki şekilde güncelledim:
// src/Twig/AppExtension.php
namespace App\Twig;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
private $translator;
private $requestStack;
public function __construct(TranslatorInterface $translator, RequestStack $requestStack)
{
$this->translator = $translator;
$this->requestStack = $requestStack;
}
public function getFilters(): array
{
return [
new TwigFilter('trans', [$this, 'trans'], ['is_safe' => ['html']]),
];
}
public function trans(string $message, array $parameters = [], string $domain = null): string
{
$request = $this->requestStack->getCurrentRequest();
if ($request) {
$domain = $domain ?: $request->attributes->get('_translation_domain', 'messages');
} else {
$domain = $domain ?: 'messages';
}
return $this->translator->trans($message, $parameters, $domain);
}
}
Artık Twig’de de çevirilerimi varsayılan translation_domain
ile kolayca gerçekleştirebiliyorum. Bu sayede hem önyüzde hem de yönetim panelinde dil desteğini daha yönetilebilir bir şekilde sunmuş oldum.