Skip to content

Instantly share code, notes, and snippets.

@tyx
Last active November 27, 2019 08:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyx/3c2695e15b67652145e64a5523397329 to your computer and use it in GitHub Desktop.
Save tyx/3c2695e15b67652145e64a5523397329 to your computer and use it in GitHub Desktop.
symfony runtime translator
<?php
declare(strict_types=1);
namespace Oxatis\Internationalization\Ui;
use Oxatis\Internationalization\Infra\RuntimeTranslator;
use Oxatis\SharedKernel\Common\Domain\Lang;
use Oxatis\ShopSettings\Infra\ShopConfigurationReader;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class CustomTranslationListener implements EventSubscriberInterface
{
private $translator;
private $shopConfigurationReader;
public function __construct(RuntimeTranslator $translator, ShopConfigurationReader $shopConfigurationReader)
{
$this->translator = $translator;
$this->shopConfigurationReader = $shopConfigurationReader;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => [
['loadCustomTranslations', 17], // Should be before LocaleListener but After SymfonyShopConfigurationListener
],
];
}
public function loadCustomTranslations(RequestEvent $event)
{
if (false === $event->isMasterRequest()) {
return;
}
$shopConfiguration = $this->shopConfigurationReader->readShopConfiguration($event->getRequest());
if (null === $shopConfiguration) {
return;
}
$lang = Lang::fromLangId($shopConfiguration->langIdOfTenant());
$this->translator->addRuntimeResource('db', $shopConfiguration->accountIdOfTenant(), $lang->toString(), 'PBFAQ');
}
}
<?php
declare(strict_types=1);
namespace Oxatis\Internationalization\Infra;
use Oxatis\Internationalization\Domain\TranslationRepository;
use Oxatis\SharedKernel\Common\Domain\Lang;
use Oxatis\SharedKernel\LegacyUri\Domain\Component;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
class DbalTranslationLoader implements LoaderInterface
{
private $translationRepository;
private $keyMapper;
public function __construct(TranslationRepository $translationRepository, TranslationKeyMapper $keyMapper)
{
$this->translationRepository = $translationRepository;
$this->keyMapper = $keyMapper;
}
public function load($resource, $locale, $domain = 'messages')
{
$accountId = is_int($resource) && 0 < $resource ? $resource : 0;
$lang = new Lang($locale);
$component = Component::ofScript($domain);
$data = $this->translationRepository->ofAccountAndComponent($accountId, $component->componentId(), $lang->id());
$keyMapping = array_flip($this->keyMapper->mappingOf($component->script()));
$messages = array_reduce(
$data,
function ($carry, $item) use ($keyMapping) {
if (false === array_key_exists($item['IDS'], $keyMapping)) {
return $carry;
}
$carry[$keyMapping[$item['IDS']]] = trim($item['String'], '[]'); // Deal with legacy IDS
return $carry;
},
[]
);
return new MessageCatalogue($locale, [$domain => $messages]);
}
}
<?php
declare(strict_types=1);
namespace Oxatis\Internationalization\Infra;
use Symfony\Component\Translation\Formatter\MessageFormatterInterface;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class RuntimeTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
{
private $innerTranslator;
private $loader;
private $runtimeResources = [];
private $cache;
private $formatter;
public function __construct($translator, DbalTranslationLoader $loader, CacheInterface $cache, MessageFormatterInterface $formatter)
{
if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
}
if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) {
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator)));
}
$this->innerTranslator = $translator;
$this->loader = $loader;
$this->cache = $cache;
$this->formatter = $formatter;
}
/**
* Passes through all unknown calls onto the translator object.
*
* @param mixed $method
* @param mixed $args
*/
public function __call($method, $args)
{
return $this->innerTranslator->{$method}(...$args);
}
public function addRuntimeResource($format, $resource, $locale, $domain = null)
{
if ('db' !== $format) {
return;
}
$this->runtimeResources[] = [$format, $resource, $locale, $domain];
}
public function getCatalogue($locale = null)
{
return $this->innerTranslator->getCatalogue($locale);
}
public function trans($id, array $parameters = [], $domain = null, $locale = null)
{
if (null === $locale) {
$locale = $this->getLocale();
}
$runtimeCatalogue = $this->getRuntimeCatalogue($locale);
if (false === $runtimeCatalogue->defines($id, $domain)) {
return $this->innerTranslator->trans(...func_get_args());
}
return $this->formatter->format($runtimeCatalogue->get($id, $domain), $locale, $parameters);
}
public function setLocale($locale)
{
return $this->innerTranslator->setLocale($locale);
}
public function getLocale()
{
return $this->innerTranslator->getLocale();
}
private function getRuntimeCatalogue($locale)
{
$catalogue = new MessageCatalogue($locale);
$runtimeResources = $this->getRuntimeResourcesOfLocale($locale);
foreach ($runtimeResources as [$format, $resource, $locale, $domain]) {
$cacheKey = implode('', [$resource, $locale, $domain]);
$runtimeCatalogue = $this->cache->get($cacheKey, function (ItemInterface $item) use ($resource, $locale, $domain) {
return $this->loader->load($resource, $locale, $domain);
});
$catalogue->addCatalogue($runtimeCatalogue);
}
return $catalogue;
}
private function getRuntimeResourcesOfLocale(string $locale): array
{
return array_filter(
$this->runtimeResources,
function ($item) use ($locale) {
return $item[2] === $locale;
}
);
}
}
services:
Oxatis\Internationalization\Infra\DbalTranslationLoader:
tags:
- { name: "translation.loader", alias: "db" }
Oxatis\Internationalization\Infra\RuntimeTranslator:
decorates: 'translator'
decoration_priority: 5
arguments:
$translator: '@Oxatis\Internationalization\Infra\RuntimeTranslator.inner'
$cache: '@cache.runtime_translations'
$formatter: '@translator.formatter.default'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment