<?php
namespace JLaso\TranslationsApiBundle\Command;
use Doctrine\ORM\EntityManager;
use JLaso\TranslationsApiBundle\Entity\Repository\TranslationRepository;
use JLaso\TranslationsApiBundle\Entity\Translation;
use JLaso\TranslationsApiBundle\Service\ClientSocketService;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use JLaso\TranslationsApiBundle\Model\CandidateKey;
/**
* @author Joseluis Laso <jlaso@joseluislaso.es>
*
* Discover new keys used on twigs, php and js and push to server
*/
class TranslationsDiscoverAndPushCommand extends ContainerAwareCommand
{
/** @var InputInterface */
private $input;
/** @var OutputInterface */
private $output;
private $srcDir;
/** @var TranslationRepository */
private $translationRepository;
/** @var EntityManager */
private $em;
/** @var ClientSocketService */
private $clientApiService;
const THROWS_EXCEPTION = true;
const ESCAPE_CHARS = '"';
/** @var array */
protected $inputFiles = array();
/** @var array */
protected $data = array();
/** @var array */
protected $filterStore = array();
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('jlaso:translations:discover-and-push');
$this->setDescription('Discover new keys used on twigs, php and js and push to server.');
$this->addOption('bundle', null, InputArgument::OPTIONAL, '--bundle=bundleName to only extract keys for this bundle');
$this->addOption('min-words', null, InputArgument::OPTIONAL, '--min-words=number of words to consider a key');
}
protected function init()
{
$this->srcDir = realpath($this->getApplication()->getKernel()->getRootDir() . '/../');
/** @var EntityManager $em */
$this->em = $this->getContainer()->get('doctrine.orm.default_entity_manager');
$this->translationRepository = $this->em->getRepository('TranslationsApiBundle:Translation');
/** @var ClientSocketService $clientApiService */
$clientApiService = $this->getContainer()->get('jlaso_translations.client.socket');
$this->clientApiService = $clientApiService;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->output = $output;
$this->init();
$verbose = (boolean)$input->getOption('verbose');
$bundleToExtract = $input->getOption('bundle');
$minWords = intval($input->getOption('min-words') ?: 0);
$this->output->writeln('<info>*** Discovering new keys in php, twig and js files ***</info>');
$fileNames = array();
$keys = array();
$idx = 0;
$numKeys = 0;
$patterns = array(
'*.twig' => '/(["\'])(?<trans>(?:\\\1|(?!\1).)+?)\1\s*\|\s*trans/i',
'*.html.twig' => '/\.trans\s*\(\s*(["\'])(?<trans>(?:\\\1|(?!\1).)+?)\1/i',
'*.php' => '/trans\s*\(\s*(["\'])(?<trans>(?:\\\1|(?!\1).)+?)\1\s*\)/i',
'*Type.php' => '/([\'"])label\1\s*=>\s*([\'"])(?<trans>(?:\\\2|(?!\2).)+?)\2/',
'*.js' => '/trans.getTranslation\s*\(\s*(["\'])(?<trans>(?:\\\1|(?!\1).)+?)\1\s*\)/i',
);
$folders = array(
$this->srcDir . '/app',
$this->srcDir . '/src'
);
$keyInfo = array();
foreach($patterns as $filePattern=>$exrPattern){
foreach($folders as $folder){
if($verbose){
$output->writeln($folder);
}
$finder = new Finder();
$files = $finder->in($folder)->name($filePattern)->files();
/** @var SplFileInfo[] $files */
foreach($files as $file){
$fileName = $folder . '/' . $file->getRelativePathname();
if(strpos($fileName, $folder . "/cache") === 0){
//$output->writeln(sprintf("ignored <comment>%s</comment>", $file));
continue;
}
if(preg_match("/\/(?P<bundle>.*Bundle)\//U", $file->getRelativePathname(), $match)){
$bundleName = $match['bundle'];
}else{
$bundleName = "*app";
}
if(!$bundleToExtract || ($bundleToExtract == $bundleName)){
$fileContents = file_get_contents($fileName);
if(preg_match_all($exrPattern, $fileContents, $matches)){
if($verbose){
$output->writeln(sprintf("<info>%s</info>", $file));
}
$fileNames[$bundleName] = $file->getRelativePathname();
if(!isset($keys[$bundleName])){
if($verbose){
$output->writeln($bundleName);
}
$keys[$bundleName] = array();
}
$keys[$bundleName] = array_merge_recursive($keys[$bundleName], $matches["trans"]);
foreach($matches['trans'] as $currentKey){
$keyInfo[$bundleName][$currentKey] = $file->getRelativePathname();
}
$numKeys += count($matches['trans']);
}
$idx++;
}
}
}
}
$output->writeln(sprintf("Total %d files examined, and found key translations in %d files", $idx, count($fileNames)));
$output->writeln(sprintf("Total %d keys extracted (%d)", $numKeys, count($keys, COUNT_RECURSIVE)));
// get all the translations from the local DB
$sortedKeys = array();
/** @var Translation[] $localKeys */
$localKeys = $this->translationRepository->findAll();
foreach($localKeys as $localKey){
$bundle = $localKey->getBundle();
$keyName = $localKey->getKey();
$sortedKeys[$keyName][$bundle] = true;
}
unset($localKeys);
$localSortedKeys = array();
/** @var CandidateKey[] $candidates */
$candidates = array();
// find keys thar are in files but not in db
foreach($keys as $bundle=>$keyArray){
foreach($keyArray as $key){
if(!isset($sortedKeys[$key]/*[$bundle]*/)){
if(!$minWords || (($minWords-1) <= substr_count($key, "."))){
$file = $keyInfo[$bundle][$key];
$candidates[] = new CandidateKey($bundle, $file, $key);
//$output->writeln(sprintf("file local key <info>%s</info>[<comment>%s</comment>] not found in DB, file %s", $bundle, $key, $file));
}
}
$localSortedKeys[$bundle][$key] = true;
}
}
// // find keys that are in db but not used in files
// foreach($sortedKeys as $bundle=>$keyArray){
//
// //var_dump($keyArray);
// foreach($keyArray as $key=>$void){
//
// if(!isset($localSortedKeys[$bundle][$key])){
//
// $output->writeln(sprintf("db key <info>%s</info>[<comment>%s</comment>] not used in local files", $bundle, $key));
// //var_dump($localSortedKeys[$bundle]);
// //die;
// }
//
// }
//
// }
if(!count($candidates)){
$output->writeln("Congratulations!, you have all the keys synchronized");
exit;
}
$output->writeln("Check the possible candidate keys to upload to server");
foreach($candidates as $candidate){
$output->writeln(
sprintf("key <info>%s</info>[<comment>%s</comment>] not found in local DB, file %s",
$candidate->getKey(),
$candidate->getBundle(),
$candidate->getFile())
);
}
$output->writeln(PHP_EOL . '<info>Have you been synchronized your translations with the sync command?</info>' . PHP_EOL);
/** @var DialogHelper $dialog */
$dialog = $this->getHelper('dialog');
if ($dialog->askConfirmation(
$output,
'<question>Confirm that you want to upload the previous keys to server ?</question>',
false
)) {
$catalog = $dialog->ask($output, 'Name of catalog in which to integrate new keys [messages]:', 'messages');
$data = array();
$date = new \DateTime();
$config = $this->getContainer()->getParameter('translations_api');
$managedLocales = $config['managed_locales'];
foreach($candidates as $candidate){
$key = $candidate->getKey();
$bundle = $candidate->getBundle();
$fileName = $candidate->getFile();
preg_match('|^(?<prefix>\w+)/'.$bundle.'/|', $fileName, $matches);
if($matches['prefix']){
$prefix = $matches['prefix'];
foreach($managedLocales as $locale){
$data[$key][$locale] = array(
'message' => '',
'updatedAt' => $date->format('c'),
'fileName' => $prefix . '/' . $this->genFileFromBundleAndLocale($bundle, $catalog, $locale),
'bundle' => $bundle,
);
}
}
}
//$this->output->writeln('uploadKeys("' . $catalog . '", $data)');
//var_dump($data); //die;
$this->clientApiService->init(/*'localhost', 10000*/);
$result = $this->clientApiService->uploadKeys($catalog, $data);
}
$output->writeln('You must now to synchronize your translations with the sync command!');
}
protected function genFileFromBundleAndLocale($bundleName, $catalog, $locale)
{
return sprintf("%s/Resources/translations/%s.%s.yml", $bundleName, $catalog, $locale);
}
/**
* @param string $message
* @throws \Exception
*/
protected function throwException($message)
{
$message = $message ?: 'Unexpected exception';
//print $message;
throw new \Exception($message);
}
}
|