Symfony'de Farklı Route’lar İçin Varsayılan Translation Domain Nasıl Ayarlanır?

Tuncay KINALI
4 min readJun 10, 2024

--

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_domainin 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:

  1. Custom bir translator servisi oluşturdum.
  2. Route’larımın request’lerine, o route boyunca kullanılacak translation_domainin adını bir varsayılan değer olarak enjekte ettim.
  3. Bir kernel event listener’ı oluşturarak custom translator’un varsayılan domainini belirledim.
  4. BaseController’ıma CustomTranslator’u ekledim.
  5. 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_domaini 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 profilerda 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.

--

--

Tuncay KINALI
0 Followers

Symfony 5'te deneyimli, Symfony 7'ye geçiş yapan bir yazılımcıyım. Next.js 14'e ilgi duyuyorum ve frontend konusuna meraklıyım.