diff --git a/composer.json b/composer.json index 9b147fd..7bb8f94 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "contributte/translation", + "name": "luksh123/translation", "description": "Symfony/Translation integration for Nette Framework.", "type": "library", "keywords": [ @@ -23,9 +23,10 @@ "nette/neon": "^3.3.1", "nette/schema": "^1.0", "nette/routing": "^3.0", - "nette/utils": "^3.2.1|~4.0.0", + "nette/utils": "^3.2.1|^4.0", "symfony/translation": "^6.0|^7.0", - "symfony/config": "^6.0|^7.0" + "symfony/config": "^6.0|^7.0", + "symfony/finder": "^6.0|^7.0" }, "require-dev": { "doctrine/orm": "^2.8", diff --git a/src/DI/TranslationExtension.php b/src/DI/TranslationExtension.php index 4ad0568..f189b1f 100644 --- a/src/DI/TranslationExtension.php +++ b/src/DI/TranslationExtension.php @@ -34,9 +34,22 @@ use Symfony\Component\Config\ConfigCacheFactory; use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Component\Translation\Provider\ProviderFactoryInterface; +use Symfony\Component\Translation\Provider\ProviderInterface; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; +use Symfony\Component\Translation\Provider\TranslationProviderCollectionFactory; +use Contributte\Translation\Dumpers\NeonFileDumper; +use Symfony\Component\Translation\Writer\TranslationWriter; +use Symfony\Component\Translation\Reader\TranslationReader; +use Symfony\Component\Translation\Command\TranslationPushCommand; +use Symfony\Component\Translation\Command\TranslationPullCommand; +use Symfony\Component\Translation\Command\XliffLintCommand; +use Symfony\Component\HttpClient\CurlHttpClient; use Tracy\IBarPanel; + /** * @property stdClass $config */ @@ -83,6 +96,7 @@ public function getConfigSchema(): Schema 'returnOriginalMessage' => Expect::bool()->default(true), 'autowired' => Expect::type('bool|array')->default(true), 'latteFactory' => Expect::string(ILatteFactory::class)->nullable(), + 'providers' => Expect::array()->default([]), ]); } @@ -187,6 +201,9 @@ public function loadConfiguration(): void $translator->setAutowired($autowired); } + $reader = $builder->addDefinition($this->prefix('providerTranslationReader')) + ->setFactory(TranslationReader::class); + // Loaders foreach ($this->config->loaders as $k1 => $v1) { $reflection = new ReflectionClass(DIHelpers::unwrapEntity($v1)); @@ -199,8 +216,76 @@ public function loadConfiguration(): void ->setFactory($v1); $translator->addSetup('addLoader', [$k1, $loader]); + $reader->addSetup('addLoader', [$k1, $loader]); + + } + + + $neonFileDumper = $builder->addDefinition($this->prefix('neonFileDumper')) + ->setFactory(NeonFileDumper::class); + + $write = $builder->addDefinition($this->prefix('providerTranslationWriter')) + ->setFactory(TranslationWriter::class) + ->addSetup('addDumper', ['neon', $neonFileDumper]); + + // Symfony providers + + $builder->addDefinition($this->prefix('providerCurlHttpClient')) + ->setFactory(CurlHttpClient::class); + + $xlfLoader = $builder->addDefinition($this->prefix('loaderXLF')) + ->setFactory(XliffFileLoader::class); + + $providers = []; + foreach ($this->config->providers as $k1 => $v1) { + + if (!isset($v1['provider'])) + throw new InvalidArgument('Missing provider parameter in definitions of providers.'); + if (!isset($v1['dsn'])) + throw new InvalidArgument('Missing DSN parameter in definitions of providers.'); + + $reflection = new ReflectionClass(DIHelpers::unwrapEntity($v1['provider'])); + + if (!$reflection->implementsInterface(ProviderFactoryInterface::class)) { + throw new InvalidArgument('Provider must implement interface "' . ProviderFactoryInterface::class . '".'); + + } + + $provider = $builder->addDefinition($this->prefix('provider' . Strings::firstUpper($k1))) + ->setFactory($v1['provider'],['defaultLocale' => $this->config->locales->default, 'loader' => $xlfLoader]); + + $providers[$k1] = $provider; + + /*if (!isset($this->config->providers[$k1]['domains']) || empty($this->config->providers[$k1]['domains'])) { + $this->config->providers[$k1]['domains'] = ['common']; + }*/ + } + $builder->addDefinition($this->prefix('providerCollectionFatory')) + ->setFactory(TranslationProviderCollectionFactory::class, ["factories" => $providers, 'enabledLocales' => $this->config->locales->whitelist]); + + + $providerCollections = $builder->addDefinition($this->prefix('providerCollection')) + ->setFactory('@'.$this->prefix('providerCollectionFatory').'::fromConfig', ['config' => $this->config->providers]); + + + // Symfony commands + $builder->addDefinition($this->prefix('translationPullCommand')) + ->setFactory(TranslationPullCommand::class, ['providerCollection' => $providerCollections, 'defaultLocale' => $this->config->locales->default, 'enabledLocales' => $this->config->locales->whitelist, 'transPaths' => $this->config->dirs]) + ->setAutowired(false); + + $builder->addDefinition($this->prefix('translationPushCommand')) + ->setFactory(TranslationPushCommand::class, ['providers' => $providerCollections, 'enabledLocales' => $this->config->locales->whitelist, 'transPaths' => $this->config->dirs]) + ->setAutowired(false); + + $builder->addDefinition($this->prefix('translationXliffLintCommand')) + ->setFactory(XliffLintCommand::class) + ->setAutowired(false); + + + + // Tracy\Panel if (!$this->config->debug || !$this->config->debugger) { return; diff --git a/src/Dumpers/NeonFileDumper.php b/src/Dumpers/NeonFileDumper.php new file mode 100644 index 0000000..f20984e --- /dev/null +++ b/src/Dumpers/NeonFileDumper.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Contributte\Translation\Dumpers; + +use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\ArrayConverter; +use Symfony\Component\Translation\Dumper\FileDumper; +use Symfony\Component\Yaml\Yaml; +use Nette\Neon\Neon as NetteNeon; + +/** + * NeonFileDumper generates yaml files from a message catalogue. + * + * @author Lukas Divacky + */ +class NeonFileDumper extends FileDumper +{ + private string $extension; + + public function __construct(string $extension = 'neon') + { + $this->extension = $extension; + } + + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string + { + if (!class_exists(NetteNeon::class)) { + throw new LogicException('Dumping translations in the neon format requires the Nette neon component.'); + } + + $data = $messages->all($domain); + + $data = ArrayConverter::expandToTree($data); + + $neon = NetteNeon::encode($data, true); + + return $neon; + + } + + protected function getExtension(): string + { + return $this->extension; + } +}