src/Shared/Infrastructure/Translation/DynamicTranslatorDecorator.php line 20

Open in your IDE?
  1. <?php
  2. namespace App\Shared\Infrastructure\Translation;
  3. use App\Infraestructure\Service\Translator\TranslationService;
  4. use App\Shared\Infrastructure\ChatGpt\ChatGptService;
  5. use Doctrine\ORM\Persisters\Exception\UnrecognizedField;
  6. use Doctrine\Persistence\Mapping\MappingException;
  7. use Symfony\Component\Filesystem\Filesystem;
  8. use Symfony\Component\Translation\TranslatorBagInterface;
  9. use Symfony\Contracts\Translation\LocaleAwareInterface;
  10. use Symfony\Contracts\Translation\TranslatorInterface;
  11. class DynamicTranslatorDecorator implements TranslatorInterfaceTranslatorBagInterfaceLocaleAwareInterface
  12. {
  13.     const TRANSLATIONS_CACHE '/translation_cache.json';
  14.     private $translatorCache;
  15.     private $domainBdCache = [];
  16.     public function __construct(
  17.         private TranslatorInterface $translator,
  18.         private TranslationService $translationService,
  19.         private ChatGptService $chatGptService,
  20.         private $projectDir,
  21.         private $repositories
  22.     )
  23.     {
  24.         if (!($translator instanceof TranslatorBagInterface && $translator instanceof LocaleAwareInterface)) {
  25.             throw new \InvalidArgumentException('The translator must implement TranslatorBagInterface and LocaleAwareInterface');
  26.         }
  27.         $fileTempTranslations = @file_get_contents($this->projectDirself::TRANSLATIONS_CACHE);
  28.         $this->translatorCache $fileTempTranslations json_decode($fileTempTranslationstrue) : [];
  29.     }
  30.     public function trans($id, array $parameters = [], $domain "messages"$locale null) {
  31.         $fallbackLocale $_ENV['LOCALE'] ?? 'es';
  32.         $locale $locale ?? $this->translator->getLocale();
  33.         $domain $this->correctDomain($domain);
  34.         $translation $this->translator->trans($id$parameters$domain$locale);
  35.         $isUsingFallback $this->isUsingFallback($id$locale$fallbackLocale$domain);
  36.         $isTranslationMissing $this->isTranslationMissing($id$locale$fallbackLocale$domain);
  37.         if (($isUsingFallback or $isTranslationMissing) and in_array($locale, ['es''eu'])) {
  38.             $translation $this->handleLocaleSpecificTranslation($id$parameters$domain$locale$fallbackLocale);
  39.             $this->updateTranslationCache($id$translation$locale$domain);
  40.         }
  41.         return $translation;
  42.     }
  43.     private function handleLocaleSpecificTranslation($id$parameters$domain$locale$fallbackLocale) {
  44.         $isUsingFallback $this->isUsingFallback($id$locale$fallbackLocale$domain);
  45.         $isTranslationMissing $this->isTranslationMissing($id$locale$fallbackLocale$domain);
  46.         if ($traduccion $this->fetchFallbackFromCache($id$domain$locale)) {
  47.             return $traduccion;
  48.         }
  49.         if ($locale === "eu") {
  50.             $sourceCache $this->fetchFallbackFromCache($id$domain$fallbackLocale);
  51.             if (!$sourceCache and $isTranslationMissing){ }
  52.             else {
  53.                 $source $sourceCache ?? $this->translator->trans($id$parameters$domain$fallbackLocale);
  54.                 if ($traduccion $this->translationService->translate($source"es-eu")) return $traduccion;
  55.             }
  56.         } elseif ($locale === "es") {
  57.             if ($traduccion $this->fetchFallbackFromDatabase($id$locale$domain)) {
  58.                 return $traduccion;
  59.             }
  60.             elseif (
  61.                 preg_match("/((^|\.)label\.|(^|\.)placeholder\.|(^|\.)form\.|^document_)/i"$id)
  62.                 and $traduccion $this->fetchChatGptTranslation($id)
  63.             ) {
  64.                 return $traduccion;
  65.             }
  66.         }
  67.         return $this->translator->trans($id$parameters$domain$locale);
  68.     }
  69.     private function fetchFallbackFromCache($id$domain$locale)
  70.     {
  71.            try {
  72.                $traducciones array_filter($this->translatorCache, fn($t) => $t['source'] == $id and $t['domain'] == $domain and $t['locale'] == $locale);
  73.                if (count($traducciones) > 0) {
  74.                    return reset($traducciones)["target"];
  75.                } else {
  76.                    return null;
  77.                }
  78.            } catch (\Exception $e) {}
  79.     }
  80.     private function correctDomain($domain) {
  81.         return strpos($domain"xml") !== false || $domain === null "messages" $domain;
  82.     }
  83.     private function updateTranslationCache($source$target$locale$domain) {
  84.         if ($this->fetchFallbackFromCache($source$domain$locale)) { return; }
  85.         if ($source == $target or !$target) { return; }
  86.         $this->translatorCache[] = ['source' => $source'target' => $target'locale' => $locale'domain' => $domain'save' => true];
  87.         $translatorCacheFilter array_filter($this->translatorCache, fn($t) => $t['save']);
  88.         file_put_contents($this->projectDirself::TRANSLATIONS_CACHEjson_encode($translatorCacheFilter));
  89.     }
  90.     private function isTranslationAvailable($id$locale$domain 'messages'): bool
  91.     {
  92.         $catalogue $this->translator->getCatalogue($locale);
  93.         return $catalogue->has($id$domain);
  94.     }
  95.     private function isUsingFallback($id$locale$fallbackLocale$domain 'messages'): bool
  96.     {
  97.         $catalogue $this->translator->getCatalogue($locale);
  98.         if ($this->hasInCurrentCatalogue($catalogue$id$domain)) {
  99.             return false;
  100.         }
  101.         $fallbackCatalogue $catalogue->getFallbackCatalogue();
  102.         if ($fallbackCatalogue && $this->hasInCurrentCatalogue($fallbackCatalogue$id$domain)) {
  103.             return true;
  104.         }
  105.         return false;
  106.     }
  107.     private function hasInCurrentCatalogue($catalogue$id$domain)
  108.     {
  109.         return isset($catalogue->all($domain)[$id]);
  110.     }
  111.     private function isTranslationMissing($id$locale$fallbackLocale$domain 'messages'): bool
  112.     {
  113.         return !$this->isTranslationAvailable($id$locale$domain) && !$this->isTranslationAvailable($id$fallbackLocale$domain);
  114.     }
  115.     public function getCatalogue($locale null)
  116.     {
  117.         return $this->translator->getCatalogue($locale);
  118.     }
  119.     public function setLocale($locale)
  120.     {
  121.         $this->translator->setLocale($locale);
  122.     }
  123.     public function getLocale()
  124.     {
  125.         return $this->translator->getLocale();
  126.     }
  127.     private function fetchChatGptTranslation($id)
  128.     {
  129.         try {
  130.             $prompt = <<<PROMPT
  131.             For the string '$id', give me a label translate in Spanish.
  132.             The label's description starts from the last period onwards; the rest is not useful.
  133.             The response will be in a JSON object in the following format:
  134.             {
  135.                 ['translate' => //translation in Spanish of the label, 'observations' => 'XXXXXX']
  136.             }
  137. PROMPT;
  138.             $traduccionResponse $this->chatGptService->__invoke($prompttrue);
  139.             if (!isset($traduccionResponse['translate'])) {
  140.                 $traduccionResponse reset($traduccionResponse);
  141.             }
  142.             $traduccion = @ucfirst($traduccionResponse['translate']) ?? null;
  143.         } catch (\Exception $e) {}
  144.         return $traduccion;
  145.     }
  146.     private function fetchFallbackFromDatabase($source$locale$domain)
  147.     {
  148.         if ($locale === 'es' and !isset($this->domainBdCache[$domain])) {
  149.             $fs = new Filesystem();
  150.             $metadata null;
  151.             foreach ($this->repositories as $repository) {
  152.                 try {
  153. //TODO Tiene daniel en load escuchando un handler y produce que el tramite aleatoria lanza la notificacion
  154. //                    if (!($object = $repository->findOneBy(['translationDomain' => $domain]))) continue;
  155. //                    $metadata = $object->getMetadata();
  156.                     break;
  157.                 } catch (UnrecognizedField|MappingException $e) {
  158.                 }
  159.             }
  160.             if (isset($metadata)) {
  161.                 $filePath $this->projectDir "/translations/$domain.$locale.xlf";
  162.                 $fs->dumpFile($filePath$metadata->getTranslations());
  163.             }
  164.             if ($fs->exists($filePath)) {
  165.                 $doc = new \DOMDocument();
  166.                 $doc->load($filePath);
  167.                 foreach ($doc->getElementsByTagName('trans-unit') as $transUnit) {
  168.                     $this->translatorCache[] = [
  169.                         'source' => $transUnit->getElementsByTagName('source')->item(0)->nodeValue,
  170.                         'target' => $transUnit->getElementsByTagName('target')->item(0)->nodeValue,
  171.                         'locale' => $locale,
  172.                         'domain' => $domain,
  173.                         'save' => false
  174.                     ];
  175.                 }
  176.                 $this->domainBdCache[$domain] = true;
  177.             }
  178.         }
  179.         return $this->fetchFallbackFromCache($source$domain$locale);
  180.     }
  181. }