/** * @param string $langPackDir language pack dir in temp folder * @param string $projectNamespace e.g. Oro, OroCRM, etc * @param string $outputFormat xml, yml, etc * @param string $locale en, en_US, fr, etc */ public function dump($langPackDir, $projectNamespace, $outputFormat, $locale) { $this->preloadExistingTranslations($locale); foreach ($this->bundles as $bundle) { // skip bundles from other projects if ($projectNamespace != $this->getBundlePrefix($bundle->getNamespace())) { continue; } $this->logger->log(LogLevel::INFO, ''); $this->logger->log(LogLevel::INFO, sprintf('Writing files for <info>%s</info>', $bundle->getName())); /** @var MessageCatalogue $currentCatalogue */ $currentCatalogue = $this->getCurrentCatalog($locale, $bundle->getName()); $extractedCatalogue = $this->extractViewTranslationKeys($locale, $bundle->getPath()); $operation = new MergeOperation($currentCatalogue, $extractedCatalogue); $messageCatalogue = $operation->getResult(); $isEmptyCatalogue = $this->validateAndFilter($messageCatalogue); if (!$isEmptyCatalogue) { $translationsDir = $langPackDir . DIRECTORY_SEPARATOR . $bundle->getName() . DIRECTORY_SEPARATOR . 'translations'; $this->filesystem->mkdir($translationsDir); $this->writer->writeTranslations($messageCatalogue, $outputFormat, ['path' => $translationsDir]); } else { $this->logger->log(LogLevel::INFO, ' - no files generated'); } } }
/** * @param $transFile * @param $resourceDirs * @param $locale * @param $loader * @param $globalCatalogue * @return MessageCatalogue */ protected function merge($transFile, &$resourceDirs, Locale $locale, TranslationLoader $loader, $globalCatalogue) { $transFileDir = dirname($transFile); if (!in_array($transFileDir, $resourceDirs) && '.' != $transFileDir) { $resourceDirs[] = $transFileDir; $currentCatalogue = new MessageCatalogue($locale->getAlias()); $loader->loadMessages($transFileDir, $currentCatalogue); $operation = new MergeOperation($globalCatalogue, $currentCatalogue); $globalCatalogue = $operation->getResult(); return $globalCatalogue; } return $globalCatalogue; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); $locale = $input->getArgument('locale'); $domain = $input->getOption('domain'); /** @var TranslationLoader $loader */ $loader = $this->getContainer()->get('translation.loader'); /** @var Kernel $kernel */ $kernel = $this->getContainer()->get('kernel'); // Define Root Path to App folder $transPaths = array($kernel->getRootDir() . '/Resources/'); // Override with provided Bundle info if (null !== $input->getArgument('bundle')) { try { $bundle = $kernel->getBundle($input->getArgument('bundle')); $transPaths = array($bundle->getPath() . '/Resources/', sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName())); } catch (\InvalidArgumentException $e) { // such a bundle does not exist, so treat the argument as path $transPaths = array($input->getArgument('bundle') . '/Resources/'); if (!is_dir($transPaths[0])) { throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); } } } elseif ($input->getOption('all')) { foreach ($kernel->getBundles() as $bundle) { $transPaths[] = $bundle->getPath() . '/Resources/'; $transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName()); } } // Extract used messages $extractedCatalogue = $this->extractMessages($locale, $transPaths); // Load defined messages $currentCatalogue = $this->loadCurrentMessages($locale, $transPaths, $loader); // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); $allMessages = $mergeOperation->getResult()->all($domain); if (null !== $domain) { $allMessages = array($domain => $allMessages); } // No defined or extracted messages if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); if (null !== $domain) { $outputMessage .= sprintf(' and domain "%s"', $domain); } $io->warning($outputMessage); return; } // Load the fallback catalogues $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths, $loader); // Display header line $headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); } $rows = array(); // Iterate all message ids and determine their state foreach ($allMessages as $domain => $messages) { foreach (array_keys($messages) as $messageId) { $value = $currentCatalogue->get($messageId, $domain); $states = array(); if ($extractedCatalogue->defines($messageId, $domain)) { if (!$currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_MISSING; } } elseif ($currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_UNUSED; } if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { continue; } foreach ($fallbackCatalogues as $fallbackCatalogue) { if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { $states[] = self::MESSAGE_EQUALS_FALLBACK; break; } } $row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); } $rows[] = $row; } } $io->table($headers, $rows); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $locale = $input->getOption('locale'); if (empty($locale)) { $locale = 'en_US'; } $domain = $input->getOption('domain'); $bundle = $input->getOption('bundle'); $log = $input->getOption('logfile'); $loader = $this->getContainer()->get('mautic.translation.loader'); $factory = $this->getContainer()->get('mautic.factory'); $viewsOnly = $input->getOption('only-views'); $dupsOnly = $input->getOption('only-dups'); $bundles = $factory->getMauticBundles(true); if (!empty($log)) { $logfile = fopen($log, 'w+'); $output = new StreamOutput($logfile, null, false, new OutputFormatter(false)); } if (!empty($bundle)) { if (!isset($bundles[$bundle])) { $output->writeln('Bundle not found'); return; } $bundles = [$bundles[$bundle]]; } // Load defined messages $currentCatalogue = new MessageCatalogue($locale); // Extract used messages from Views $extractedCatalogue = new MessageCatalogue($locale); $phpFormExtractor = new PhpFormTranslationExtractor(); foreach ($bundles as $bundle) { if (!$dupsOnly) { if (file_exists($bundle['directory'] . '/Views')) { $phpFormExtractor->extract($bundle['directory'] . '/Views', $extractedCatalogue); $this->getContainer()->get('translation.extractor')->extract($bundle['directory'] . '/Views', $extractedCatalogue); } if (!$viewsOnly) { $directories = ['/Form/Type', '/EventListener', '/Model', '/EventListener', '/Controller']; foreach ($directories as $d) { if (file_exists($bundle['directory'] . $d)) { $phpFormExtractor->extract($bundle['directory'] . $d, $extractedCatalogue); $this->getContainer()->get('translation.extractor')->extract($bundle['directory'] . $d, $extractedCatalogue); } } } } if (is_dir($bundle['directory'] . '/Translations')) { $currentCatalogue = $loader->load(null, $locale, $domain); } } // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); $allMessages = $mergeOperation->getResult()->all($domain); if (null !== $domain) { $allMessages = [$domain => $allMessages]; } // No defined or extracted messages if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { $outputMessage = sprintf('<info>No defined or extracted messages for locale "%s"</info>', $locale); if (null !== $domain) { $outputMessage .= sprintf(' <info>and domain "%s"</info>', $domain); } $output->writeln($outputMessage); return; } /** @var \Symfony\Component\Console\Helper\Table $table */ $table = new Table($output); // Display header line $headers = ['State(s)', 'Id', sprintf('Message Preview (%s)', $locale)]; $table->setHeaders($headers); $duplicateCheck = []; // Iterate all message ids and determine their state foreach ($allMessages as $domain => $messages) { foreach (array_keys($messages) as $messageId) { $value = $currentCatalogue->get($messageId, $domain); $duplicateKey = strtolower($value); if (!isset($duplicateCheck[$duplicateKey])) { $duplicateCheck[$duplicateKey] = []; } $duplicateCheck[$duplicateKey][] = ['id' => $messageId, 'domain' => $domain]; $states = []; if ($extractedCatalogue->defines($messageId, $domain)) { if (!$currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_MISSING; } } elseif ($currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_UNUSED; } if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { continue; } $row = [$this->formatStates($states), $this->formatId($messageId), $this->sanitizeString($value)]; $table->addRow($row); } } if (!$dupsOnly) { $table->render(); $output->writeln(''); $output->writeln('<info>Legend:</info>'); $output->writeln(sprintf(' %s Missing message', $this->formatState(self::MESSAGE_MISSING))); $output->writeln(sprintf(' %s Unused message', $this->formatState(self::MESSAGE_UNUSED))); $output->writeln(sprintf(' %s Same as the fallback message', $this->formatState(self::MESSAGE_EQUALS_FALLBACK))); } $output->writeln(''); $output->writeln('<info>Duplicates:</info>'); /** @var \Symfony\Component\Console\Helper\Table $table */ $table = new Table($output); // Display header line $headers = ['Value', 'Domain', 'Message ID']; $table->setHeaders($headers); //Check for duplicates $totalDuplicateCount = 0; foreach ($duplicateCheck as $value => $dups) { $count = count($dups); if ($count > 1) { ++$totalDuplicateCount; $table->addRow(['', '', '']); $table->addRow([$this->sanitizeString($value), $count, '']); foreach ($dups as $dup) { $table->addRow(['', $dup['domain'], $dup['id']]); } } } $table->render(); $output->writeln(''); $output->writeln('<info>Total number of duplicates: ' . $totalDuplicateCount . '</info>'); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $locale = $input->getArgument('locale'); $domain = $input->getOption('domain'); $kernel = $this->getContainer()->get('kernel'); $bundle = $kernel->getBundle($input->getArgument('bundle')); $loader = $this->getContainer()->get('translation.loader'); // Extract used messages $extractedCatalogue = new MessageCatalogue($locale); $bundlePaths = array($bundle->getPath() . '/Resources/', sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName())); foreach ($bundlePaths as $path) { $path = $path . 'views'; if (is_dir($path)) { $this->getContainer()->get('translation.extractor')->extract($path, $extractedCatalogue); } } // Load defined messages $currentCatalogue = new MessageCatalogue($locale); foreach ($bundlePaths as $path) { $path = $path . 'translations'; if (is_dir($path)) { $loader->loadMessages($path, $currentCatalogue); } } // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); $allMessages = $mergeOperation->getResult()->all($domain); if (null !== $domain) { $allMessages = array($domain => $allMessages); } // No defined or extracted messages if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { $outputMessage = sprintf('<info>No defined or extracted messages for locale "%s"</info>', $locale); if (null !== $domain) { $outputMessage .= sprintf(' <info>and domain "%s"</info>', $domain); } $output->writeln($outputMessage); return; } // Load the fallback catalogues $fallbackCatalogues = array(); $translator = $this->getContainer()->get('translator'); if ($translator instanceof Translator) { foreach ($translator->getFallbackLocales() as $fallbackLocale) { if ($fallbackLocale === $locale) { continue; } $fallbackCatalogue = new MessageCatalogue($fallbackLocale); $loader->loadMessages($bundle->getPath() . '/Resources/translations', $fallbackCatalogue); $fallbackCatalogues[] = $fallbackCatalogue; } } if (class_exists('Symfony\\Component\\Console\\Helper\\Table')) { $table = new Table($output); } else { $table = $this->getHelperSet()->get('table'); } // Display header line $headers = array('State(s)', 'Id', sprintf('Message Preview (%s)', $locale)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); } $table->setHeaders($headers); // Iterate all message ids and determine their state foreach ($allMessages as $domain => $messages) { foreach (array_keys($messages) as $messageId) { $value = $currentCatalogue->get($messageId, $domain); $states = array(); if ($extractedCatalogue->defines($messageId, $domain)) { if (!$currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_MISSING; } } elseif ($currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_UNUSED; } if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { continue; } foreach ($fallbackCatalogues as $fallbackCatalogue) { if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { $states[] = self::MESSAGE_EQUALS_FALLBACK; break; } } $row = array($this->formatStates($states), $this->formatId($messageId), $this->sanitizeString($value)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); } $table->addRow($row); } } if (class_exists('Symfony\\Component\\Console\\Helper\\Table')) { $table->render(); } else { $table->render($output); } $output->writeln(''); $output->writeln('<info>Legend:</info>'); $output->writeln(sprintf(' %s Missing message', $this->formatState(self::MESSAGE_MISSING))); $output->writeln(sprintf(' %s Unused message', $this->formatState(self::MESSAGE_UNUSED))); $output->writeln(sprintf(' %s Same as the fallback message', $this->formatState(self::MESSAGE_EQUALS_FALLBACK))); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $output = new SymfonyStyle($input, $output); if (false !== strpos($input->getFirstArgument(), ':d')) { $output->caution('The use of "translation:debug" command is deprecated since version 2.7 and will be removed in 3.0. Use the "debug:translation" instead.'); } $locale = $input->getArgument('locale'); $domain = $input->getOption('domain'); $loader = $this->getContainer()->get('translation.loader'); $kernel = $this->getContainer()->get('kernel'); // Define Root Path to App folder $rootPath = $kernel->getRootDir(); // Override with provided Bundle info if (null !== $input->getArgument('bundle')) { try { $rootPath = $kernel->getBundle($input->getArgument('bundle'))->getPath(); } catch (\InvalidArgumentException $e) { // such a bundle does not exist, so treat the argument as path $rootPath = $input->getArgument('bundle'); if (!is_dir($rootPath)) { throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $rootPath)); } } } // get bundle directory $translationsPath = $rootPath . '/Resources/translations'; // Extract used messages $extractedCatalogue = new MessageCatalogue($locale); if (is_dir($rootPath . '/Resources/views')) { $this->getContainer()->get('translation.extractor')->extract($rootPath . '/Resources/views', $extractedCatalogue); } // Load defined messages $currentCatalogue = new MessageCatalogue($locale); if (is_dir($translationsPath)) { $loader->loadMessages($translationsPath, $currentCatalogue); } // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); $allMessages = $mergeOperation->getResult()->all($domain); if (null !== $domain) { $allMessages = array($domain => $allMessages); } // No defined or extracted messages if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); if (null !== $domain) { $outputMessage .= sprintf(' and domain "%s"', $domain); } $output->warning($outputMessage); return; } // Load the fallback catalogues $fallbackCatalogues = array(); $translator = $this->getContainer()->get('translator'); if ($translator instanceof Translator) { foreach ($translator->getFallbackLocales() as $fallbackLocale) { if ($fallbackLocale === $locale) { continue; } $fallbackCatalogue = new MessageCatalogue($fallbackLocale); $loader->loadMessages($translationsPath, $fallbackCatalogue); $fallbackCatalogues[] = $fallbackCatalogue; } } // Display header line $headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); } $rows = array(); // Iterate all message ids and determine their state foreach ($allMessages as $domain => $messages) { foreach (array_keys($messages) as $messageId) { $value = $currentCatalogue->get($messageId, $domain); $states = array(); if ($extractedCatalogue->defines($messageId, $domain)) { if (!$currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_MISSING; } } elseif ($currentCatalogue->defines($messageId, $domain)) { $states[] = self::MESSAGE_UNUSED; } if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { continue; } foreach ($fallbackCatalogues as $fallbackCatalogue) { if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { $states[] = self::MESSAGE_EQUALS_FALLBACK; break; } } $row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value)); foreach ($fallbackCatalogues as $fallbackCatalogue) { $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); } $rows[] = $row; } } $output->table($headers, $rows); }
public function execute() { $locales = $this->getLocaleManager()->getActiveList(); $errors = []; foreach ($locales as $locale) { if (!in_array($locale->getAlias(), ['ru', 'en'])) { continue; } $globalCatalogue = new MessageCatalogue($locale->getAlias()); $resourceDirs = []; foreach ($this->getTranslator()->getResources() as $resources) { foreach ($resources as $transFile) { if ($transFile[0] == DIRECTORY_SEPARATOR) { $transFileDir = dirname($transFile); if (!in_array($transFileDir, $resourceDirs)) { $resourceDirs[] = $transFileDir; $currentCatalogue = new MessageCatalogue($locale->getAlias()); $this->getTranslationLoader()->loadMessages($transFileDir, $currentCatalogue); $operation = new MergeOperation($globalCatalogue, $currentCatalogue); $globalCatalogue = $operation->getResult(); } } } } $localeErrors = $this->compareDbWithFiles($globalCatalogue); $errors[$locale->getAlias()] = $localeErrors; } foreach ($errors as $localeAlias => $errorLocaleData) { if (count($errorLocaleData) > 0) { $this->getLogger()->info('<info>' . strtoupper($localeAlias) . ' update:</info>'); $this->getLogger()->info('use OctavaTranslationDbMigrateTrait;'); foreach ($errorLocaleData as $domain => $errorData) { $this->getLogger()->error('$this->deleteTranslations(\'' . $domain . '\', [\'' . implode("', '", $errorData) . '\']);'); } } } $count = 0; foreach ($errors as $locale => $errorLocaleData) { foreach ($errorLocaleData as $domain => $errorData) { $count += count($errorData); } $this->getLogger()->info('<info>' . strtoupper($locale) . '</info> - found errors: ' . $count); } return $count ? 1 : 0; }