Skip to content

Commit

Permalink
TMP
Browse files Browse the repository at this point in the history
  • Loading branch information
Aeliot-Tm committed Dec 2, 2024
1 parent 12ff8b8 commit 9216409
Showing 1 changed file with 259 additions and 0 deletions.
259 changes: 259 additions & 0 deletions src/Command/DetectMissedTranslationsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<?php

declare(strict_types=1);

namespace Aeliot\Bundle\TransMaintain\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Translation\Catalogue\MergeOperation;
use Symfony\Component\Translation\DataCollectorTranslator;
use Symfony\Component\Translation\Extractor\ExtractorInterface;
use Symfony\Component\Translation\LoggingTranslator;
use Symfony\Component\Translation\MessageCatalogue;
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
use Symfony\Component\Translation\Translator;
use Symfony\Contracts\Translation\TranslatorInterface;

final class DetectMissedTranslationsCommand extends Command
{
private const MESSAGE_MISSING = 0;

Check failure on line 25 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Constant Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::MESSAGE_MISSING is unused.
private const MESSAGE_UNUSED = 1;

Check failure on line 26 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Constant Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::MESSAGE_UNUSED is unused.
private const MESSAGE_EQUALS_FALLBACK = 2;

Check failure on line 27 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Constant Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::MESSAGE_EQUALS_FALLBACK is unused.

private ?string $defaultTransPath;
private ?string $defaultViewsPath;
private ExtractorInterface $extractor;
private TranslationReaderInterface $reader;
private TranslatorInterface $translator;
private array $transPaths;

Check failure on line 34 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Property Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::$transPaths type has no value type specified in iterable type array.
private array $viewsPaths;

Check failure on line 35 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Property Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::$viewsPaths is unused.

Check failure on line 35 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Property Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::$viewsPaths type has no value type specified in iterable type array.

public function __construct(

Check failure on line 37 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::__construct() has parameter $transPaths with no value type specified in iterable type array.
TranslatorInterface $translator,
TranslationReaderInterface $reader,
ExtractorInterface $extractor,
string $defaultTransPath = null,
string $defaultViewsPath = null,
array $transPaths = []
//, DirectoryProvider $directoryProvider = null
) {
parent::__construct('aeliot_trans_maintain:missed:detect');

$this->extractor = $extractor;
$this->translator = $translator;
$this->reader = $reader;
$this->defaultTransPath = $defaultTransPath;
$this->defaultViewsPath = $defaultViewsPath;
$this->transPaths = $transPaths;
}

protected function configure(): void
{
$this->setDescription('Command extracts used translations and detect missed');
$this->addArgument('locale', InputArgument::REQUIRED, 'The locale');
$this->addArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages');
$this->addOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain');
$this->addOption('view', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'View paths', []);
$this->addOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages');
$this->addOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages');
$this->addOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$locale = $input->getArgument('locale');
$domain = $input->getOption('domain');
$viewsPaths = $this->getViewPaths($input);
$transPaths = $this->getTransPaths();

// Load defined messages
$currentCatalogue = $this->loadCurrentMessages($locale, $transPaths);

// Extract used messages
$extractedCatalogue = $this->extractMessages($locale, $viewsPaths);

// Merge defined and extracted messages to get all message ids
$allMessages = $this->getAllMessages($extractedCatalogue, $currentCatalogue, $domain);

// No defined or extracted messages
if (empty($allMessages) || (null !== $domain && empty($allMessages[$domain]))) {
$io->getErrorStyle()->warning($this->createNoMessagesNotification($locale, $domain));

return 0;
}

// Display header line
$headers = ['State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)];
$rows = $this->getTableRows($allMessages, $extractedCatalogue, $currentCatalogue);

$io->table($headers, $rows);

return 0;
}

private function formatId(string $id): string
{
return sprintf('<fg=cyan;options=bold>%s</>', $id);
}

private function sanitizeString(string $string, int $length = 40): string
{
$string = trim(preg_replace('/\s+/', ' ', $string));

if (false !== $encoding = mb_detect_encoding($string, null, true)) {
if (mb_strlen($string, $encoding) > $length) {
return mb_substr($string, 0, $length - 3, $encoding).'...';
}
} elseif (\strlen($string) > $length) {
return substr($string, 0, $length - 3).'...';
}

return $string;
}

private function extractMessages(string $locale, array $transPaths): MessageCatalogue

Check failure on line 122 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::extractMessages() has parameter $transPaths with no value type specified in iterable type array.
{
$extractedCatalogue = new MessageCatalogue($locale);
foreach ($transPaths as $path) {
if (is_dir($path) || is_file($path)) {
$this->extractor->extract($path, $extractedCatalogue);
}
}

return $extractedCatalogue;
}

private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue

Check failure on line 134 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::loadCurrentMessages() has parameter $transPaths with no value type specified in iterable type array.
{
$currentCatalogue = new MessageCatalogue($locale);
foreach ($transPaths as $path) {
if (is_dir($path)) {
$this->reader->read($path, $currentCatalogue);
}
}

return $currentCatalogue;
}

/**
* @return MessageCatalogue[]
*/
private function loadFallbackCatalogues(string $locale, array $transPaths): array

Check failure on line 149 in src/Command/DetectMissedTranslationsCommand.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Aeliot\Bundle\TransMaintain\Command\DetectMissedTranslationsCommand::loadFallbackCatalogues() has parameter $transPaths with no value type specified in iterable type array.
{
$fallbackCatalogues = [];
if ($this->translator instanceof Translator || $this->translator instanceof DataCollectorTranslator || $this->translator instanceof LoggingTranslator) {
foreach ($this->translator->getFallbackLocales() as $fallbackLocale) {
if ($fallbackLocale === $locale) {
continue;
}

$fallbackCatalogue = new MessageCatalogue($fallbackLocale);
foreach ($transPaths as $path) {
if (is_dir($path)) {
$this->reader->read($path, $fallbackCatalogue);
}
}
$fallbackCatalogues[] = $fallbackCatalogue;
}
}

return $fallbackCatalogues;
}

private function createNoMessagesNotification($locale, $domain): string
{
$outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale);

if (null !== $domain) {
$outputMessage .= sprintf(' and domain "%s"', $domain);
}

return $outputMessage;
}

private function getViewPaths(InputInterface $input): array
{
$viewsPaths = (array) ($input->getOption('view') ?: []);
if (!$viewsPaths && $this->defaultViewsPath) {
$viewsPaths[] = $this->defaultViewsPath;
}

return $viewsPaths;
}

private function getTransPaths(): array
{
/** @var KernelInterface $kernel */
$kernel = $this->getApplication()->getKernel();

// Define Root Paths
$transPaths = $this->transPaths;
if ($this->defaultTransPath) {
$transPaths[] = $this->defaultTransPath;
}

// Override with provided Bundle info
foreach ($kernel->getBundles() as $bundle) {
$bundleDir = $bundle->getPath();

$transPaths[] = is_dir($bundleDir.'/Resources/translations')
? $bundleDir.'/Resources/translations'
: $bundleDir.'/translations';
}

return $transPaths;
}

/**
* @return array<string,mixed>
*/
private function getAllMessages(
MessageCatalogue $extractedCatalogue,
MessageCatalogue $currentCatalogue,
?string $domain
): array {
$mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);
$allMessages = $mergeOperation->getResult()->all($domain);
if (null !== $domain) {
$allMessages = [$domain => $allMessages];
}

return $allMessages;
}

/**
* @param array<string,string> $allMessages
*
* @return string[]
*/
private function getTableRows(
array $allMessages,
MessageCatalogue $extractedCatalogue,
MessageCatalogue $currentCatalogue
): array {
$rows = [];
// Iterate all message ids and determine their state
foreach ($allMessages as $domain => $messages) {
foreach (array_keys($messages) as $messageId) {
if (!$extractedCatalogue->defines($messageId, $domain)
|| $currentCatalogue->defines($messageId, $domain)
) {
continue;
}

$value = $currentCatalogue->get($messageId, $domain);
$rows[] = [$domain, $this->formatId($messageId), $this->sanitizeString($value)];
}
}

return $rows;
}
}

0 comments on commit 9216409

Please sign in to comment.