diff --git a/src/Oro/Bundle/TranslationBundle/Command/OroTranslationPackCommand.php b/src/Oro/Bundle/TranslationBundle/Command/OroTranslationPackCommand.php
index 97bb8d6cdce..c67aa66236d 100644
--- a/src/Oro/Bundle/TranslationBundle/Command/OroTranslationPackCommand.php
+++ b/src/Oro/Bundle/TranslationBundle/Command/OroTranslationPackCommand.php
@@ -31,7 +31,7 @@ protected function configure()
->setDescription('Dump translation messages and optionally upload them to third-party service')
->setDefinition(
array(
- new InputArgument('project', InputArgument::REQUIRED, 'The project [e.g Oro, OroCRM etc]'),
+ new InputArgument('project', InputArgument::OPTIONAL, 'The project [e.g Oro, OroCRM etc]'),
new InputArgument(
'locale',
InputArgument::OPTIONAL,
@@ -94,6 +94,12 @@ protected function configure()
InputOption::VALUE_NONE,
'Download all language packs from project at translation service'
),
+ new InputOption(
+ 'force',
+ 'f',
+ InputOption::VALUE_NONE,
+ 'Usage with --upload-mode update option. Replace all translation file on crowdin'
+ )
)
)
->setHelp(
@@ -146,6 +152,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
if ($input->getOption('upload') === true) {
+ if (!$input->getArgument('project')) {
+ $output->writeln('Input argument "project" is required');
+
+ return 1;
+ }
$translationService = $this->getTranslationService($input, $output);
$langPackDirs = [];
@@ -153,10 +164,17 @@ protected function execute(InputInterface $input, OutputInterface $output)
$langPackDirs[$namespace] = $this->getLangPackDir($namespace);
}
- $this->upload($translationService, $input->getOption('upload-mode'), $langPackDirs);
+ $force = (bool) $input->getOption('force');
+
+ $this->upload($translationService, $input->getOption('upload-mode'), $langPackDirs, $force);
}
if ($input->getOption('download') === true) {
+ if (!$input->getArgument('project')) {
+ $output->writeln('Input argument "project" is required');
+
+ return 1;
+ }
$this->download($input, $output);
}
@@ -167,13 +185,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
* @param TranslationServiceProvider $translationService
* @param string $mode
* @param array $languagePackPath one or few dirs
+ * @param bool $force
*
* @return array
*/
- protected function upload(TranslationServiceProvider $translationService, $mode, $languagePackPath)
+ protected function upload(TranslationServiceProvider $translationService, $mode, $languagePackPath, $force = false)
{
if ('update' == $mode) {
- $translationService->update($languagePackPath);
+ $translationService->update($languagePackPath, $force);
} else {
$translationService->upload($languagePackPath);
}
@@ -264,7 +283,7 @@ protected function getAdapterFromInput(InputInterface $input)
*/
protected function dump($projectNamespace, $locale, OutputInterface $output, $outputFormat)
{
- $output->writeln(sprintf('Dumping language pack for %s', $projectNamespace));
+ $output->writeln('Dumping language pack');
$container = $this->getContainer();
$dumper = new TranslationPackDumper(
@@ -272,14 +291,13 @@ protected function dump($projectNamespace, $locale, OutputInterface $output, $ou
$container->get('translation.extractor'),
$container->get('translation.loader'),
new Filesystem(),
- $container->get('kernel')->getBundles()
+ $container->get('kernel')->getBundles(),
+ $container->get('oro_translation.utils.translation_dump_helper')
);
$dumper->setLogger(new OutputLogger($output));
- $languagePackPath = $this->getLangPackDir($projectNamespace);
$dumper->dump(
- $languagePackPath,
- $projectNamespace,
+ $this->path,
$outputFormat,
$locale
);
diff --git a/src/Oro/Bundle/TranslationBundle/Provider/AbstractAPIAdapter.php b/src/Oro/Bundle/TranslationBundle/Provider/AbstractAPIAdapter.php
index 652d73c3d43..12ac02c924f 100644
--- a/src/Oro/Bundle/TranslationBundle/Provider/AbstractAPIAdapter.php
+++ b/src/Oro/Bundle/TranslationBundle/Provider/AbstractAPIAdapter.php
@@ -112,18 +112,19 @@ protected function getFileFolders(array $files)
$dirs = [];
foreach ($files as $remotePath) {
- $subFolders = explode(DIRECTORY_SEPARATOR, dirname($remotePath));
+ $remotePath = str_replace(DIRECTORY_SEPARATOR, '/', dirname($remotePath));
+ $subFolders = array_filter(explode('/', $remotePath));
$currentDir = [];
foreach ($subFolders as $folderName) {
$currentDir[] = $folderName;
// crowdin understand only "/" as directory separator
- $path = implode('/', $currentDir);
- $dirs[] = $path;
+ $path = implode('/', $currentDir);
+ $dirs[] = $path;
}
}
- return $dirs;
+ return array_unique($dirs);
}
}
diff --git a/src/Oro/Bundle/TranslationBundle/Provider/CrowdinAdapter.php b/src/Oro/Bundle/TranslationBundle/Provider/CrowdinAdapter.php
index 32174a24e5a..d8e91245b1d 100644
--- a/src/Oro/Bundle/TranslationBundle/Provider/CrowdinAdapter.php
+++ b/src/Oro/Bundle/TranslationBundle/Provider/CrowdinAdapter.php
@@ -64,8 +64,9 @@ public function createDirectories($dirs)
{
$this->logger->info('Creating directories');
+ $current = 1;
+
foreach ($dirs as $index => $dir) {
- $current = $index + 1;
$result = $this->addDirectory($dir);
if (false == $result['success'] && isset($result['error'])) {
@@ -82,13 +83,15 @@ public function createDirectories($dirs)
sprintf('%0.2f%% [created] %s directory', $current * 100 / count($dirs), $dir)
);
}
+
+ $current++;
}
return $this;
}
/**
- * @param string $files
+ * @param array $files
* @param string $mode 'add' or 'update'
*
* @return bool
diff --git a/src/Oro/Bundle/TranslationBundle/Provider/TranslationPackDumper.php b/src/Oro/Bundle/TranslationBundle/Provider/TranslationPackDumper.php
index 30f9213f537..bf4f18420a4 100644
--- a/src/Oro/Bundle/TranslationBundle/Provider/TranslationPackDumper.php
+++ b/src/Oro/Bundle/TranslationBundle/Provider/TranslationPackDumper.php
@@ -16,6 +16,8 @@
use Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader;
+use Oro\Bundle\TranslationBundle\Utils\TranslationDumpHelper;
+
class TranslationPackDumper implements LoggerAwareInterface
{
/** @var TranslationWriter */
@@ -39,19 +41,24 @@ class TranslationPackDumper implements LoggerAwareInterface
/** @var MessageCatalogue[] Translations loaded from yaml files, existing */
protected $loadedTranslations = [];
+ /** @var TranslationDumpHelper */
+ protected $helper;
+
/**
- * @param TranslationWriter $writer
- * @param ExtractorInterface $extractor
- * @param TranslationLoader $loader
- * @param Filesystem $filesystem
- * @param array $bundles
+ * @param TranslationWriter $writer
+ * @param ExtractorInterface $extractor
+ * @param TranslationLoader $loader
+ * @param Filesystem $filesystem
+ * @param array $bundles
+ * @param TranslationDumpHelper $helper
*/
public function __construct(
TranslationWriter $writer,
ExtractorInterface $extractor,
TranslationLoader $loader,
Filesystem $filesystem,
- array $bundles
+ array $bundles,
+ TranslationDumpHelper $helper
) {
$this->writer = $writer;
$this->extractor = $extractor;
@@ -59,6 +66,7 @@ public function __construct(
$this->filesystem = $filesystem;
$this->bundles = $bundles;
$this->logger = new NullLogger();
+ $this->helper = $helper;
}
/**
@@ -70,17 +78,17 @@ public function setLogger(LoggerInterface $logger)
}
/**
- * @param string $langPackDir language pack dir in temp folder
- * @param string $projectNamespace e.g. Oro, OroCRM, etc
+ * @param string $systemPath system path for get language temp folder
* @param string $outputFormat xml, yml, etc
* @param string $locale en, en_US, fr, etc
*/
- public function dump($langPackDir, $projectNamespace, $outputFormat, $locale)
+ public function dump($systemPath, $outputFormat, $locale)
{
$this->preloadExistingTranslations($locale);
foreach ($this->bundles as $bundle) {
// skip bundles from other projects
- if ($projectNamespace != $this->getBundlePrefix($bundle->getNamespace())) {
+ $namespace = $this->getBundlePrefix($bundle->getNamespace());
+ if (!preg_match('/^Oro.+/', $namespace)) {
continue;
}
@@ -95,7 +103,10 @@ public function dump($langPackDir, $projectNamespace, $outputFormat, $locale)
$messageCatalogue = $operation->getResult();
$isEmptyCatalogue = $this->validateAndFilter($messageCatalogue);
+ $messageCatalogue = $this->helper->removeDuplicate($bundle->getName(), $messageCatalogue);
if (!$isEmptyCatalogue) {
+ $langPackDir = $this->helper->getLangPackDir($systemPath, $namespace);
+
$translationsDir = $langPackDir . DIRECTORY_SEPARATOR .
$bundle->getName() . DIRECTORY_SEPARATOR . 'translations';
$this->filesystem->mkdir($translationsDir);
diff --git a/src/Oro/Bundle/TranslationBundle/Provider/TranslationServiceProvider.php b/src/Oro/Bundle/TranslationBundle/Provider/TranslationServiceProvider.php
index 96c3fba7760..34385fac778 100644
--- a/src/Oro/Bundle/TranslationBundle/Provider/TranslationServiceProvider.php
+++ b/src/Oro/Bundle/TranslationBundle/Provider/TranslationServiceProvider.php
@@ -69,15 +69,18 @@ public function __construct(
* merge generated files over downloaded and upload result back to remote
*
* @param array|string[] $dirs
+ * @param bool $force
+ *
+ * @return void
*/
- public function update($dirs)
+ public function update($dirs, $force)
{
$targetDir = $this->getTmpDir('oro-trans');
$pathToSave = $targetDir . DIRECTORY_SEPARATOR . 'update';
$targetDir = $targetDir . DIRECTORY_SEPARATOR . self::DEFAULT_SOURCE_LOCALE . DIRECTORY_SEPARATOR;
$isDownloaded = $this->download($pathToSave, [], self::DEFAULT_SOURCE_LOCALE, false);
- if (!$isDownloaded) {
+ if (!$isDownloaded && !$force) {
return false;
}
@@ -89,7 +92,7 @@ public function update($dirs)
$localContents = file($fileInfo);
$remoteFile = $targetDir . $fileInfo->getRelativePathname();
- if (file_exists($remoteFile)) {
+ if (file_exists($remoteFile) && !$force) {
$remoteContents = file($remoteFile);
array_shift($remoteContents); // remove dashes from the beginning of file
} else {
@@ -115,20 +118,31 @@ public function update($dirs)
/**
* Upload translations
*
- * @param string $dir
+ * @param string|array $dirs
* @param string $mode
*
* @return mixed
*/
- public function upload($dir, $mode = 'add')
+ public function upload($dirs, $mode = 'add')
{
- $finder = Finder::create()->files()->name('*.yml')->in($dir);
+ $dirs = $this->processDirs($dirs);
+
+ $finder = Finder::create()->files()->name('*.yml')->in($dirs);
/** $file \SplFileInfo */
$files = [];
foreach ($finder->files() as $file) {
+ $apiPath = (string)$file;
+ foreach ($dirs as $dir) {
+ if (strpos($apiPath, $dir) !== false) {
+ $apiPath = str_replace($dir, '', $apiPath);
+ break;
+ }
+ }
+
// crowdin understand only "/" as directory separator :)
- $apiPath = str_replace([$dir, DIRECTORY_SEPARATOR], ['', '/'], (string)$file);
+ $apiPath = str_replace(DIRECTORY_SEPARATOR, '/', $apiPath);
+
$files[$apiPath] = (string)$file;
}
@@ -171,7 +185,23 @@ public function download($pathToSave, array $projects, $locale = null, $toApply
$this->cleanup($targetDir);
}
- return $isExtracted && $isDownloaded;
+ return $isExtracted;
+ }
+
+ /**
+ * @param string|array $dirs
+ * @return array
+ */
+ protected function processDirs($dirs)
+ {
+ $dirs = is_array($dirs) ? $dirs : [$dirs];
+
+ return array_map(
+ function ($path) {
+ return rtrim($path, DIRECTORY_SEPARATOR);
+ },
+ $dirs
+ );
}
/**
diff --git a/src/Oro/Bundle/TranslationBundle/Resources/config/services.yml b/src/Oro/Bundle/TranslationBundle/Resources/config/services.yml
index 9ae199e22d8..2faac997d00 100644
--- a/src/Oro/Bundle/TranslationBundle/Resources/config/services.yml
+++ b/src/Oro/Bundle/TranslationBundle/Resources/config/services.yml
@@ -187,3 +187,6 @@ services:
class: '%oro_translation.strategy.provider.class%'
arguments:
- '@oro_translation.strategy.default'
+
+ oro_translation.utils.translation_dump_helper:
+ class: Oro\Bundle\TranslationBundle\Utils\TranslationDumpHelper
diff --git a/src/Oro/Bundle/TranslationBundle/Resources/doc/reference/commands.md b/src/Oro/Bundle/TranslationBundle/Resources/doc/reference/commands.md
index d11b9ccd731..ccecfeb0131 100644
--- a/src/Oro/Bundle/TranslationBundle/Resources/doc/reference/commands.md
+++ b/src/Oro/Bundle/TranslationBundle/Resources/doc/reference/commands.md
@@ -73,7 +73,13 @@ app/console oro:translation:pack --dump OroCRM
```
**Upload translation pack:**
-Note: you must call dump command before using this one, otherwise system won't have anything to upload or will upload earlier generated files if there were left.
+Note: you must call dump command before using this one, otherwise system won't have anything to upload or will upload earlier generated files if there were left. This command with "-m update" option without "--force" option does't replace data on crowdin only add strings.
```bash
app/console oro:translation:pack -i project-key -k abc1234567890c23ee33a767adb --upload OroCRM
```
+**Upload and replace translation pack on crowdin:**
+The flag "force" tell that you want to replaced all crowdin translation with local translation
+Note: the --force option works only with "-m update" option
+```bash
+app/console oro:translation:pack -i project-key -k abc1234567890c23ee33a767adb --upload OroCRM -m update --force
+```
diff --git a/src/Oro/Bundle/TranslationBundle/Tests/Unit/Command/OroTranslationPackCommandTest.php b/src/Oro/Bundle/TranslationBundle/Tests/Unit/Command/OroTranslationPackCommandTest.php
index bb47b374327..6a228b1eaec 100644
--- a/src/Oro/Bundle/TranslationBundle/Tests/Unit/Command/OroTranslationPackCommandTest.php
+++ b/src/Oro/Bundle/TranslationBundle/Tests/Unit/Command/OroTranslationPackCommandTest.php
@@ -82,10 +82,9 @@ public function executeInputProvider()
'error if project not specified' => array(
array('--dump' => true),
array(
- 'dump' => 0,
+ 'dump' => 1,
'upload' => 0
- ),
- '\RuntimeException'
+ )
),
'dump action should perform' => array(
array('--dump' => true, 'project' => 'SomeProject'),
diff --git a/src/Oro/Bundle/TranslationBundle/Tests/Unit/Provider/TranslationServiceTest.php b/src/Oro/Bundle/TranslationBundle/Tests/Unit/Provider/TranslationServiceTest.php
index 1692dfb7abd..6b07ef4d303 100644
--- a/src/Oro/Bundle/TranslationBundle/Tests/Unit/Provider/TranslationServiceTest.php
+++ b/src/Oro/Bundle/TranslationBundle/Tests/Unit/Provider/TranslationServiceTest.php
@@ -96,7 +96,7 @@ public function testUpload()
/**
* @dataProvider updateDataProvider
*/
- public function testUpdate($isDownloaded, $downloadFileExists)
+ public function testUpdateWithoutForce($isDownloaded, $downloadFileExists)
{
$service = $this->getServiceMock(
['download', 'upload', 'cleanup'],
@@ -131,7 +131,7 @@ function ($pathToSave) {
->method('download')
->will($this->returnValue(false));
- $this->assertFalse($service->update($dir));
+ $this->assertFalse($service->update($dir, false));
return;
}
@@ -142,7 +142,7 @@ function ($pathToSave) {
$service->expects($this->once())
->method('cleanup');
- $service->update([$dir]);
+ $service->update([$dir], false);
}
public function testDownload()
diff --git a/src/Oro/Bundle/TranslationBundle/Utils/TranslationDumpHelper.php b/src/Oro/Bundle/TranslationBundle/Utils/TranslationDumpHelper.php
new file mode 100644
index 00000000000..58c9e43e349
--- /dev/null
+++ b/src/Oro/Bundle/TranslationBundle/Utils/TranslationDumpHelper.php
@@ -0,0 +1,97 @@
+messages[$domain][$bundle] = $messages;
+
+ return $this;
+ }
+
+ /**
+ * @param string $bundle
+ * @param MessageCatalogue $messageCatalogue
+ * @param string $domain
+ * @return MessageCatalogue
+ */
+ public function removeDuplicate($bundle, MessageCatalogue $messageCatalogue, $domain = 'messages')
+ {
+ if (isset($this->messages[$domain])) {
+ foreach ($this->messages[$domain] as $messages) {
+ $data = array_intersect_key($messageCatalogue->all($domain), $messages);
+ if (count($data)) {
+ $this->setDuplicate($bundle, $data);
+ $messageCatalogue->replace(array_diff_key($messageCatalogue->all($domain), $data), $domain);
+ }
+ }
+ }
+
+ $this->setMessages($bundle, $messageCatalogue->all($domain), $domain);
+
+ return $messageCatalogue;
+ }
+
+ /**
+ * Return lang pack location
+ *
+ * @param string $systemPath
+ * @param string $projectNamespace
+ * @param null|string $bundleName
+ *
+ * @return string
+ */
+ public function getLangPackDir($systemPath, $projectNamespace, $bundleName = null)
+ {
+ $path = $systemPath . $projectNamespace . DIRECTORY_SEPARATOR;
+
+ if (!is_null($bundleName)) {
+ $path .= $bundleName . DIRECTORY_SEPARATOR . 'translations';
+ }
+
+ return $path;
+ }
+
+ /**
+ * @param string $key
+ * @param array $data
+ *
+ * @return TranslationDumpHelper
+ */
+ protected function setDuplicate($key, array $data)
+ {
+ $this->duplicates[$key] = isset($this->duplicates[$key])
+ ? array_merge($this->duplicates[$key], $data)
+ : $data;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDuplicates()
+ {
+ return $this->duplicates;
+ }
+}