name: TranslationDebugCommand class_comment: null dependencies: - name: AsCommand type: class source: Symfony\Component\Console\Attribute\AsCommand - name: Command type: class source: Symfony\Component\Console\Command\Command - name: CompletionInput type: class source: Symfony\Component\Console\Completion\CompletionInput - name: CompletionSuggestions type: class source: Symfony\Component\Console\Completion\CompletionSuggestions - name: InvalidArgumentException type: class source: Symfony\Component\Console\Exception\InvalidArgumentException - name: InputArgument type: class source: Symfony\Component\Console\Input\InputArgument - name: InputInterface type: class source: Symfony\Component\Console\Input\InputInterface - name: InputOption type: class source: Symfony\Component\Console\Input\InputOption - name: OutputInterface type: class source: Symfony\Component\Console\Output\OutputInterface - name: SymfonyStyle type: class source: Symfony\Component\Console\Style\SymfonyStyle - name: KernelInterface type: class source: Symfony\Component\HttpKernel\KernelInterface - name: MergeOperation type: class source: Symfony\Component\Translation\Catalogue\MergeOperation - name: DataCollectorTranslator type: class source: Symfony\Component\Translation\DataCollectorTranslator - name: ExtractorInterface type: class source: Symfony\Component\Translation\Extractor\ExtractorInterface - name: LoggingTranslator type: class source: Symfony\Component\Translation\LoggingTranslator - name: MessageCatalogue type: class source: Symfony\Component\Translation\MessageCatalogue - name: TranslationReaderInterface type: class source: Symfony\Component\Translation\Reader\TranslationReaderInterface - name: Translator type: class source: Symfony\Component\Translation\Translator - name: TranslatorInterface type: class source: Symfony\Contracts\Translation\TranslatorInterface properties: [] methods: - name: loadFallbackCatalogues visibility: private parameters: - name: locale - name: transPaths comment: "# * Helps finding unused or missing translation messages in a given locale\n\ # * and comparing them with the fallback ones.\n# *\n# * @author Florian Voutzinos\ \ \n# *\n# * @final\n# */\n# #[AsCommand(name: 'debug:translation',\ \ description: 'Display translation messages information')]\n# class TranslationDebugCommand\ \ extends Command\n# {\n# public const EXIT_CODE_GENERAL_ERROR = 64;\n# public\ \ const EXIT_CODE_MISSING = 65;\n# public const EXIT_CODE_UNUSED = 66;\n# public\ \ const EXIT_CODE_FALLBACK = 68;\n# public const MESSAGE_MISSING = 0;\n# public\ \ const MESSAGE_UNUSED = 1;\n# public const MESSAGE_EQUALS_FALLBACK = 2;\n# \n\ # public function __construct(\n# private TranslatorInterface $translator,\n#\ \ private TranslationReaderInterface $reader,\n# private ExtractorInterface $extractor,\n\ # private ?string $defaultTransPath = null,\n# private ?string $defaultViewsPath\ \ = null,\n# private array $transPaths = [],\n# private array $codePaths = [],\n\ # private array $enabledLocales = [],\n# ) {\n# parent::__construct();\n# }\n\ # \n# protected function configure(): void\n# {\n# $this\n# ->setDefinition([\n\ # new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),\n# new InputArgument('bundle',\ \ InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),\n\ # new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'),\n\ # new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Display only\ \ missing messages'),\n# new InputOption('only-unused', null, InputOption::VALUE_NONE,\ \ 'Display only unused messages'),\n# new InputOption('all', null, InputOption::VALUE_NONE,\ \ 'Load messages from all registered bundles'),\n# ])\n# ->setHelp(<<<'EOF'\n\ # The %command.name% command helps finding unused or missing translation\n\ # messages and comparing them with the fallback ones by inspecting the\n# templates\ \ and translation files of a given bundle or the default translations directory.\n\ # \n# You can display information about bundle translations in a specific locale:\n\ # \n# php %command.full_name% en AcmeDemoBundle\n# \n# You can also\ \ specify a translation domain for the search:\n# \n# php %command.full_name%\ \ --domain=messages en AcmeDemoBundle\n# \n# You can only display missing\ \ messages:\n# \n# php %command.full_name% --only-missing en AcmeDemoBundle\n\ # \n# You can only display unused messages:\n# \n# php %command.full_name%\ \ --only-unused en AcmeDemoBundle\n# \n# You can display information about\ \ application translations in a specific locale:\n# \n# php %command.full_name%\ \ en\n# \n# You can display information about translations in all registered\ \ bundles in a specific locale:\n# \n# php %command.full_name% --all en\n\ # \n# EOF\n# )\n# ;\n# }\n# \n# protected function execute(InputInterface $input,\ \ OutputInterface $output): int\n# {\n# $io = new SymfonyStyle($input, $output);\n\ # \n# $locale = $input->getArgument('locale');\n# $domain = $input->getOption('domain');\n\ # \n# $exitCode = self::SUCCESS;\n# \n# /** @var KernelInterface $kernel */\n\ # $kernel = $this->getApplication()->getKernel();\n# \n# // Define Root Paths\n\ # $transPaths = $this->getRootTransPaths();\n# $codePaths = $this->getRootCodePaths($kernel);\n\ # \n# // Override with provided Bundle info\n# if (null !== $input->getArgument('bundle'))\ \ {\n# try {\n# $bundle = $kernel->getBundle($input->getArgument('bundle'));\n\ # $bundleDir = $bundle->getPath();\n# $transPaths = [is_dir($bundleDir.'/Resources/translations')\ \ ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations'];\n# $codePaths\ \ = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates'];\n\ # if ($this->defaultTransPath) {\n# $transPaths[] = $this->defaultTransPath;\n\ # }\n# if ($this->defaultViewsPath) {\n# $codePaths[] = $this->defaultViewsPath;\n\ # }\n# } catch (\\InvalidArgumentException) {\n# // such a bundle does not exist,\ \ so treat the argument as path\n# $path = $input->getArgument('bundle');\n# \n\ # $transPaths = [$path.'/translations'];\n# $codePaths = [$path.'/templates'];\n\ # \n# if (!is_dir($transPaths[0])) {\n# throw new InvalidArgumentException(\\\ sprintf('\"%s\" is neither an enabled bundle nor a directory.', $transPaths[0]));\n\ # }\n# }\n# } elseif ($input->getOption('all')) {\n# foreach ($kernel->getBundles()\ \ as $bundle) {\n# $bundleDir = $bundle->getPath();\n# $transPaths[] = is_dir($bundleDir.'/Resources/translations')\ \ ? $bundleDir.'/Resources/translations' : $bundle->getPath().'/translations';\n\ # $codePaths[] = is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views'\ \ : $bundle->getPath().'/templates';\n# }\n# }\n# \n# // Extract used messages\n\ # $extractedCatalogue = $this->extractMessages($locale, $codePaths);\n# \n# //\ \ Load defined messages\n# $currentCatalogue = $this->loadCurrentMessages($locale,\ \ $transPaths);\n# \n# // Merge defined and extracted messages to get all message\ \ ids\n# $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);\n\ # $allMessages = $mergeOperation->getResult()->all($domain);\n# if (null !== $domain)\ \ {\n# $allMessages = [$domain => $allMessages];\n# }\n# \n# // No defined or\ \ extracted messages\n# if (!$allMessages || null !== $domain && empty($allMessages[$domain]))\ \ {\n# $outputMessage = \\sprintf('No defined or extracted messages for locale\ \ \"%s\"', $locale);\n# \n# if (null !== $domain) {\n# $outputMessage .= \\sprintf('\ \ and domain \"%s\"', $domain);\n# }\n# \n# $io->getErrorStyle()->warning($outputMessage);\n\ # \n# return self::EXIT_CODE_GENERAL_ERROR;\n# }\n# \n# // Load the fallback catalogues\n\ # $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths);\n\ # \n# // Display header line\n# $headers = ['State', 'Domain', 'Id', \\sprintf('Message\ \ Preview (%s)', $locale)];\n# foreach ($fallbackCatalogues as $fallbackCatalogue)\ \ {\n# $headers[] = \\sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale());\n\ # }\n# $rows = [];\n# // Iterate all message ids and determine their state\n#\ \ foreach ($allMessages as $domain => $messages) {\n# foreach (array_keys($messages)\ \ as $messageId) {\n# $value = $currentCatalogue->get($messageId, $domain);\n\ # $states = [];\n# \n# if ($extractedCatalogue->defines($messageId, $domain))\ \ {\n# if (!$currentCatalogue->defines($messageId, $domain)) {\n# $states[] =\ \ self::MESSAGE_MISSING;\n# \n# if (!$input->getOption('only-unused')) {\n# $exitCode\ \ |= self::EXIT_CODE_MISSING;\n# }\n# }\n# } elseif ($currentCatalogue->defines($messageId,\ \ $domain)) {\n# $states[] = self::MESSAGE_UNUSED;\n# \n# if (!$input->getOption('only-missing'))\ \ {\n# $exitCode |= self::EXIT_CODE_UNUSED;\n# }\n# }\n# \n# if (!\\in_array(self::MESSAGE_UNUSED,\ \ $states, true) && $input->getOption('only-unused')\n# || !\\in_array(self::MESSAGE_MISSING,\ \ $states, true) && $input->getOption('only-missing')\n# ) {\n# continue;\n# }\n\ # \n# foreach ($fallbackCatalogues as $fallbackCatalogue) {\n# if ($fallbackCatalogue->defines($messageId,\ \ $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) {\n# $states[]\ \ = self::MESSAGE_EQUALS_FALLBACK;\n# \n# $exitCode |= self::EXIT_CODE_FALLBACK;\n\ # \n# break;\n# }\n# }\n# \n# $row = [$this->formatStates($states), $domain, $this->formatId($messageId),\ \ $this->sanitizeString($value)];\n# foreach ($fallbackCatalogues as $fallbackCatalogue)\ \ {\n# $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain));\n\ # }\n# \n# $rows[] = $row;\n# }\n# }\n# \n# $io->table($headers, $rows);\n# \n\ # return $exitCode;\n# }\n# \n# public function complete(CompletionInput $input,\ \ CompletionSuggestions $suggestions): void\n# {\n# if ($input->mustSuggestArgumentValuesFor('locale'))\ \ {\n# $suggestions->suggestValues($this->enabledLocales);\n# \n# return;\n# }\n\ # \n# /** @var KernelInterface $kernel */\n# $kernel = $this->getApplication()->getKernel();\n\ # \n# if ($input->mustSuggestArgumentValuesFor('bundle')) {\n# $availableBundles\ \ = [];\n# foreach ($kernel->getBundles() as $bundle) {\n# $availableBundles[]\ \ = $bundle->getName();\n# \n# if ($extension = $bundle->getContainerExtension())\ \ {\n# $availableBundles[] = $extension->getAlias();\n# }\n# }\n# \n# $suggestions->suggestValues($availableBundles);\n\ # \n# return;\n# }\n# \n# if ($input->mustSuggestOptionValuesFor('domain')) {\n\ # $locale = $input->getArgument('locale');\n# \n# $mergeOperation = new MergeOperation(\n\ # $this->extractMessages($locale, $this->getRootCodePaths($kernel)),\n# $this->loadCurrentMessages($locale,\ \ $this->getRootTransPaths())\n# );\n# \n# $suggestions->suggestValues($mergeOperation->getDomains());\n\ # }\n# }\n# \n# private function formatState(int $state): string\n# {\n# if (self::MESSAGE_MISSING\ \ === $state) {\n# return ' missing ';\n# }\n# \n# if (self::MESSAGE_UNUSED\ \ === $state) {\n# return ' unused ';\n# }\n# \n# if (self::MESSAGE_EQUALS_FALLBACK\ \ === $state) {\n# return ' fallback ';\n# }\n# \n# return $state;\n\ # }\n# \n# private function formatStates(array $states): string\n# {\n# $result\ \ = [];\n# foreach ($states as $state) {\n# $result[] = $this->formatState($state);\n\ # }\n# \n# return implode(' ', $result);\n# }\n# \n# private function formatId(string\ \ $id): string\n# {\n# return \\sprintf('%s', $id);\n\ # }\n# \n# private function sanitizeString(string $string, int $length = 40):\ \ string\n# {\n# $string = trim(preg_replace('/\\s+/', ' ', $string));\n# \n#\ \ if (false !== $encoding = mb_detect_encoding($string, null, true)) {\n# if (mb_strlen($string,\ \ $encoding) > $length) {\n# return mb_substr($string, 0, $length - 3, $encoding).'...';\n\ # }\n# } elseif (\\strlen($string) > $length) {\n# return substr($string, 0, $length\ \ - 3).'...';\n# }\n# \n# return $string;\n# }\n# \n# private function extractMessages(string\ \ $locale, array $transPaths): MessageCatalogue\n# {\n# $extractedCatalogue =\ \ new MessageCatalogue($locale);\n# foreach ($transPaths as $path) {\n# if (is_dir($path)\ \ || is_file($path)) {\n# $this->extractor->extract($path, $extractedCatalogue);\n\ # }\n# }\n# \n# return $extractedCatalogue;\n# }\n# \n# private function loadCurrentMessages(string\ \ $locale, array $transPaths): MessageCatalogue\n# {\n# $currentCatalogue = new\ \ MessageCatalogue($locale);\n# foreach ($transPaths as $path) {\n# if (is_dir($path))\ \ {\n# $this->reader->read($path, $currentCatalogue);\n# }\n# }\n# \n# return\ \ $currentCatalogue;\n# }\n# \n# /**\n# * @return MessageCatalogue[]" - name: getRootTransPaths visibility: private parameters: [] comment: null - name: getRootCodePaths visibility: private parameters: - name: kernel comment: null traits: - Symfony\Component\Console\Attribute\AsCommand - Symfony\Component\Console\Command\Command - Symfony\Component\Console\Completion\CompletionInput - Symfony\Component\Console\Completion\CompletionSuggestions - Symfony\Component\Console\Exception\InvalidArgumentException - Symfony\Component\Console\Input\InputArgument - Symfony\Component\Console\Input\InputInterface - Symfony\Component\Console\Input\InputOption - Symfony\Component\Console\Output\OutputInterface - Symfony\Component\Console\Style\SymfonyStyle - Symfony\Component\HttpKernel\KernelInterface - Symfony\Component\Translation\Catalogue\MergeOperation - Symfony\Component\Translation\DataCollectorTranslator - Symfony\Component\Translation\Extractor\ExtractorInterface - Symfony\Component\Translation\LoggingTranslator - Symfony\Component\Translation\MessageCatalogue - Symfony\Component\Translation\Reader\TranslationReaderInterface - Symfony\Component\Translation\Translator - Symfony\Contracts\Translation\TranslatorInterface interfaces: []