diff --git a/src/Bundle/ChillMainBundle/Command/DetectMissingTranslationsCommand.php b/src/Bundle/ChillMainBundle/Command/DetectMissingTranslationsCommand.php
new file mode 100644
index 000000000..12a194699
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Command/DetectMissingTranslationsCommand.php
@@ -0,0 +1,199 @@
+setDescription('Checks for missing translations in the specified locale.')
+ ->addOption('bundle', null, InputOption::VALUE_REQUIRED, 'The relative path to the translation files', 'translations')
+ ->addOption('locale', null, InputOption::VALUE_REQUIRED, 'The locale to check for missing translations', 'fr');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $locale = $input->getOption('locale');
+ $bundle = $input->getOption('bundle');
+ $bundlePath = self::BASE_DIR . '/' . $bundle;
+// $vuePath = $bundlePath . '/Resources/public/vuejs';
+ $twigPath = $bundlePath . '/Resources/public';
+ $localeFile = $bundlePath . "/translations/messages.$locale.yml";
+
+ if (!file_exists($localeFile)) {
+ $output->writeln("The locale file '$localeFile' does not exist.");
+ return Command::FAILURE;
+ }
+
+ $missingKeys = [];
+
+ // Load existing translations for the specified locale
+ $existingTranslations = Yaml::parseFile($localeFile);
+
+ // Extract translation keys from Vue components
+/* $vueKeys = $this->extractVueKeys($vuePath);
+ foreach ($vueKeys as $key) {
+ if (!$this->keyExistsInTranslations($key, $existingTranslations)) {
+ $missingKeys[$key] = 'Missing translation in Vue component';
+ }
+ }*/
+
+ // Extract translation keys from Twig templates
+ $twigKeys = $this->extractTwigKeys($twigPath);
+ foreach ($twigKeys as $key) {
+ if (!$this->keyExistsInTranslations($key, $existingTranslations)) {
+ $missingKeys[$key] = 'Missing translation in Twig template';
+ }
+ }
+
+ // Extract translation keys from PHP code
+ $phpKeys = $this->extractPhpKeys($bundlePath);
+ foreach ($phpKeys as $key) {
+ if (!$this->keyExistsInTranslations($key, $existingTranslations)) {
+ $missingKeys[$key] = 'Missing translation in PHP code';
+ }
+ }
+
+ if (empty($missingKeys)) {
+ $output->writeln("No missing translations found.");
+ } else {
+ $output->writeln("Missing translations:");
+ foreach ($missingKeys as $key => $info) {
+ $output->writeln("$key: $info");
+ }
+
+ // Prompt user to add missing translations
+ $this->promptForTranslations($missingKeys, $localeFile);
+ }
+
+ return Command::SUCCESS;
+ }
+
+ private function extractVueKeys($path)
+ {
+ $keys = [];
+ $files = $this->getAllFiles($path, ['vue']);
+
+ foreach ($files as $file) {
+ $content = file_get_contents($file);
+ preg_match_all('/\$t\((\'|")(.*?)(\'|")\)/', $content, $matches);
+ foreach ($matches[2] as $match) {
+ $keys[] = $match;
+ }
+ }
+
+ return array_unique($keys);
+ }
+
+ private function extractTwigKeys($path)
+ {
+ $keys = [];
+ $files = $this->getAllFiles($path, ['html.twig', 'twig']);
+
+ foreach ($files as $file) {
+ $content = file_get_contents($file);
+ preg_match_all('/\|trans\s*\(\'(.*?)\'/', $content, $matches);
+ foreach ($matches[1] as $match) {
+ $keys[] = $match;
+ }
+ }
+
+ return array_unique($keys);
+ }
+
+ private function extractPhpKeys($path)
+ {
+ $keys = [];
+ $files = $this->getAllFiles($path, ['php']);
+
+ foreach ($files as $file) {
+ $content = file_get_contents($file);
+ preg_match_all('/->trans\((\'|")(.*?)(\'|")\)/', $content, $matches);
+ foreach ($matches[2] as $match) {
+ $keys[] = $match;
+ }
+ }
+
+ return array_unique($keys);
+ }
+
+ private function getAllFiles($directory, array $extensions)
+ {
+ $files = [];
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
+
+ foreach ($iterator as $file) {
+ if ($file->isFile() && in_array($file->getExtension(), $extensions)) {
+ $files[] = $file->getPathname();
+ }
+ }
+
+ return $files;
+ }
+
+ private function keyExistsInTranslations($key, $translations)
+ {
+ $keys = explode('.', $key);
+ $current = $translations;
+
+ foreach ($keys as $part) {
+ if (isset($current[$part])) {
+ $current = $current[$part];
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function promptForTranslations($missingKeys, $localeFile)
+ {
+ $fs = new Filesystem();
+ foreach ($missingKeys as $key => $info) {
+ $translation = readline("Enter translation for '$key': ");
+ if ($translation !== '') {
+ // Add translation to the YAML file
+ $existing = file_exists($localeFile) ? Yaml::parseFile($localeFile) : [];
+ $this->addTranslation($existing, $key, $translation);
+ $fs->dumpFile($localeFile, Yaml::dump($existing));
+ }
+ }
+ }
+
+ private function addTranslation(&$translations, $key, $translation)
+ {
+ $keys = explode('.', $key);
+ $current = &$translations;
+
+ foreach ($keys as $part) {
+ if (!isset($current[$part])) {
+ $current[$part] = [];
+ }
+ $current = &$current[$part];
+ }
+
+ $current = $translation;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php b/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php
new file mode 100644
index 000000000..56ccebba2
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php
@@ -0,0 +1,132 @@
+setDescription('Detects duplicate translations in YAML files.')
+ ->addOption('path', null, InputOption::VALUE_REQUIRED, 'The directory or file path containing translation files', 'translations');
+// ->addOption('output', null, InputOption::VALUE_OPTIONAL, 'The file path to write the output to', 'php://stdout');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $relativePath = $input->getOption('path');
+ $basePath = self::BASE_DIR;
+ $fullPath = $basePath . '/' . $relativePath;
+
+// $outputPath = $input->getOption('output');
+
+ if (!file_exists($fullPath)) {
+ $output->writeln("The path '$fullPath' does not exist.");
+ return Command::FAILURE;
+ }
+
+ $translationMap = [];
+
+ if (is_dir($fullPath)) {
+ // Process all YAML files in the directory
+ foreach (glob($fullPath . '/*.yaml') as $file) {
+ $this->processFile($file, $translationMap);
+ }
+ } elseif (is_file($fullPath)) {
+ // Process the specific YAML file
+ $this->processFile($fullPath, $translationMap);
+ } else {
+ $output->writeln("The path '$fullPath' is neither a directory nor a file.");
+ return Command::FAILURE;
+ }
+
+ $duplicates = array_filter($translationMap, function($keys) {
+ return count($keys) > 1;
+ });
+
+ if (empty($duplicates)) {
+ $output->writeln("No duplicate translations found.");
+ } else {
+ $output->writeln("Duplicate translations found:");
+ foreach ($duplicates as $translation => $keys) {
+ $output->writeln("Translation: '$translation' is used in keys: " . implode(', ', $keys));
+ }
+ }
+
+/* $outputText = "";
+ if (empty($duplicates)) {
+ $outputText .= "No duplicate translations found.\n";
+ } else {
+ $outputText .= "Duplicate translations found:\n";
+ foreach ($duplicates as $translation => $keys) {
+ $outputText .= "Translation: '$translation' is used in keys: " . implode(', ', $keys) . "\n";
+ }
+ }
+
+ // Write output to the specified file or to stdout
+ if ($outputPath === 'php://stdout') {
+ $output->writeln($outputText);
+ } else {
+ try {
+ file_put_contents($outputPath, $outputText);
+ $output->writeln("Output written to '$outputPath'");
+ } catch (\Exception $e) {
+ $output->writeln("Failed to write to '$outputPath': " . $e->getMessage() . "");
+ return Command::FAILURE;
+ }
+ }*/
+
+ return Command::SUCCESS;
+ }
+
+ private function processFile($file, &$translationMap): void
+ {
+ try {
+ $translations = Yaml::parseFile($file);
+
+ // Flatten the array to handle nested keys
+ $this->flattenArray($translations, '', $translationMap);
+
+ } catch (\Exception $e) {
+ // Handle YAML parsing exceptions
+ // You might want to log the error or notify the user
+ }
+ }
+
+ private function flattenArray(array $array, $prefix, &$translationMap): void
+ {
+ foreach ($array as $key => $value) {
+ $fullKey = $prefix ? $prefix . '.' . $key : $key;
+ if (is_array($value)) {
+ // Recursively process nested arrays
+ $this->flattenArray($value, $fullKey, $translationMap);
+ } else {
+ // Handle translation value
+ if (isset($translationMap[$value])) {
+ $translationMap[$value][] = $fullKey;
+ } else {
+ $translationMap[$value] = [$fullKey];
+ }
+ }
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml
index 94cb2cf97..106b01f37 100644
--- a/src/Bundle/ChillMainBundle/config/services/command.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/command.yaml
@@ -74,3 +74,11 @@ services:
Chill\MainBundle\Command\SynchronizeEntityInfoViewsCommand:
tags:
- {name: console.command}
+
+ Chill\MainBundle\Command\DetectTranslationDuplicatesCommand:
+ tags:
+ - { name: console.command }
+
+ Chill\MainBundle\Command\DetectMissingTranslationsCommand:
+ tags:
+ - { name: console.command }