diff options
author | sgiehl <stefan@matomo.org> | 2021-03-03 20:01:46 +0300 |
---|---|---|
committer | sgiehl <stefan@matomo.org> | 2021-03-03 20:03:18 +0300 |
commit | 5e2c5dce5cd88ba3872a72ae3e7675514e3a6098 (patch) | |
tree | e349abc16ccb2394cda9fc20d072cc28d271a312 /plugins/LanguagesManager | |
parent | 4aa36276203768513e888a394112f5acbb3da5c7 (diff) |
Adds command to validate translations
Diffstat (limited to 'plugins/LanguagesManager')
-rw-r--r-- | plugins/LanguagesManager/Commands/SetTranslations.php | 22 | ||||
-rw-r--r-- | plugins/LanguagesManager/Commands/Validate.php | 142 | ||||
-rw-r--r-- | plugins/LanguagesManager/TranslationWriter/Writer.php | 20 |
3 files changed, 181 insertions, 3 deletions
diff --git a/plugins/LanguagesManager/Commands/SetTranslations.php b/plugins/LanguagesManager/Commands/SetTranslations.php index cc62c84656..6790d40158 100644 --- a/plugins/LanguagesManager/Commands/SetTranslations.php +++ b/plugins/LanguagesManager/Commands/SetTranslations.php @@ -31,7 +31,8 @@ class SetTranslations extends TranslationBase ->setDescription('Sets new translations for a given language') ->addOption('code', 'c', InputOption::VALUE_REQUIRED, 'code of the language to set translations for') ->addOption('file', 'f', InputOption::VALUE_REQUIRED, 'json file to load new translations from') - ->addOption('plugin', 'pl', InputOption::VALUE_OPTIONAL, 'optional name of plugin to set translations for'); + ->addOption('plugin', 'pl', InputOption::VALUE_OPTIONAL, 'optional name of plugin to set translations for') + ->addOption('validate', '', InputOption::VALUE_OPTIONAL, 'when set, the file will not be written, but validated. The given value will be used as filename to write filter results to.'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -97,7 +98,24 @@ class SetTranslations extends TranslationBase return; } - $translationWriter->save(); + if ($input->getOption('validate')) { + $translationWriter->applyFilters(); + $filteredData = $translationWriter->getFilteredData(); + unset($filteredData[EmptyTranslations::class]); + + if (!empty($filteredData)) { + $content = "Filtered File: ".($plugin??'Base')." / ". $languageCode ."\n"; + foreach ($filteredData as $filter => $data) { + $content .= "- Filtered by: $filter\n"; + $content .= json_encode($data, JSON_PRETTY_PRINT); + $content .= "\n"; + } + $content .= "\n\n"; + file_put_contents($input->getOption('validate'), $content, FILE_APPEND); + } + } else { + $translationWriter->save(); + } $output->writeln("Finished."); } diff --git a/plugins/LanguagesManager/Commands/Validate.php b/plugins/LanguagesManager/Commands/Validate.php new file mode 100644 index 0000000000..07082ba730 --- /dev/null +++ b/plugins/LanguagesManager/Commands/Validate.php @@ -0,0 +1,142 @@ +<?php +/** + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\LanguagesManager\Commands; + +use Piwik\Plugin\Manager; +use Piwik\Plugins\LanguagesManager\API; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + */ +class Validate extends TranslationBase +{ + protected function configure() + { + $this->setName('translations:validate') + ->setDescription('Validates translation files') + ->addOption('username', 'u', InputOption::VALUE_OPTIONAL, 'Transifex username') + ->addOption('password', 'p', InputOption::VALUE_OPTIONAL, 'Transifex password') + ->addOption('slug', 's', InputOption::VALUE_OPTIONAL, 'Transifex project slug') + ->addOption('all', 'a', InputOption::VALUE_NONE, 'Force to update all plugins (even non core). Can not be used with plugin option') + ->addOption('plugin', 'P', InputOption::VALUE_OPTIONAL, 'optional name of plugin to update translations for'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->setDecorated(true); + + $start = microtime(true); + + $languages = API::getInstance()->getAvailableLanguageNames(); + + $languageCodes = array(); + foreach ($languages as $languageInfo) { + $languageCodes[] = $languageInfo['code']; + } + + $plugin = $input->getOption('plugin'); + + $pluginList = array($plugin); + if (empty($plugin)) { + $pluginList = self::getAllPlugins(); + array_unshift($pluginList, ''); + } + + file_put_contents(PIWIK_DOCUMENT_ROOT . '/filter.txt', ''); + + foreach ($pluginList as $plugin) { + + $output->writeln(""); + + // fetch base or specific plugin + $this->fetchTranslations($input, $output, $plugin); + + $files = _glob(FetchTranslations::getDownloadPath() . DIRECTORY_SEPARATOR . '*.json'); + + if (count($files) == 0) { + $output->writeln("No translation updates available! Skipped."); + continue; + } + + foreach ($files as $filename) { + + $code = basename($filename, '.json'); + + $command = $this->getApplication()->find('translations:set'); + $arguments = array( + 'command' => 'translations:set', + '--code' => $code, + '--file' => $filename, + '--plugin' => $plugin, + '--validate' => PIWIK_DOCUMENT_ROOT . '/filter.txt' + ); + $inputObject = new ArrayInput($arguments); + $inputObject->setInteractive($input->isInteractive()); + $command->run($inputObject, $output); + } + + $output->writeln(''); + } + + $output->writeln("Finished in " . round(microtime(true)-$start, 3) . "s"); + } + + /** + * Returns all plugins having their own translations that are bundled in core + * @return array + */ + public static function getAllPlugins() + { + static $pluginsWithTranslations; + + if (!empty($pluginsWithTranslations)) { + return $pluginsWithTranslations; + } + + $pluginsWithTranslations = array(); + foreach (Manager::getPluginsDirectories() as $pluginsDir) { + $pluginsWithTranslations = array_merge($pluginsWithTranslations, glob(sprintf('%s*/lang/en.json', $pluginsDir))); + } + $pluginsWithTranslations = array_map(function ($elem) { + $replace = Manager::getPluginsDirectories(); + $replace[] = '/lang/en.json'; + return str_replace($replace, '', $elem); + }, $pluginsWithTranslations); + + return $pluginsWithTranslations; + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param string $plugin + * @throws \Exception + */ + protected function fetchTranslations(InputInterface $input, OutputInterface $output, $plugin) + { + + $command = $this->getApplication()->find('translations:fetch'); + $arguments = array( + 'command' => 'translations:fetch', + '--username' => $input->getOption('username'), + '--password' => $input->getOption('password'), + '--slug' => $input->getOption('slug'), + '--plugin' => $plugin, + '--lastupdate' => 1 + ); + + $inputObject = new ArrayInput($arguments); + $inputObject->setInteractive($input->isInteractive()); + $command->run($inputObject, $output); + } +} diff --git a/plugins/LanguagesManager/TranslationWriter/Writer.php b/plugins/LanguagesManager/TranslationWriter/Writer.php index eb957f9494..c720087711 100644 --- a/plugins/LanguagesManager/TranslationWriter/Writer.php +++ b/plugins/LanguagesManager/TranslationWriter/Writer.php @@ -70,6 +70,13 @@ class Writer */ protected $filterMessages = array(); + /** + * Data that had been changed by filters + * + * @var array + */ + protected $filteredData = array(); + const UNFILTERED = 'unfiltered'; const FILTERED = 'filtered'; @@ -338,6 +345,16 @@ class Writer } /** + * Returns the cleaning errors + * + * @return array + */ + public function getFilteredData() + { + return $this->filteredData; + } + + /** * @param FilterAbstract $filter */ public function addFilter(FilterAbstract $filter) @@ -350,7 +367,7 @@ class Writer * * @return bool error state */ - protected function applyFilters() + public function applyFilters() { // skip if already cleaned if ($this->currentState == self::FILTERED) { @@ -373,6 +390,7 @@ class Writer $filteredData = $filter->getFilteredData(); if (!empty($filteredData)) { $this->filterMessages[] = get_class($filter) . " changed: " . var_export($filteredData, 1); + $this->filteredData[get_class($filter)] = $filteredData; } } |