diff --git a/src/Command/ExportTranslationsCommand.php b/src/Command/ExportTranslationsCommand.php
new file mode 100644
index 0000000..da18488
--- /dev/null
+++ b/src/Command/ExportTranslationsCommand.php
@@ -0,0 +1,145 @@
+setName('objectbg:translation:export')
+ ->setDefinition(
+ array(
+ new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
+ new InputArgument(
+ 'bundle',
+ InputArgument::OPTIONAL,
+ 'The bundle name or directory where to load the messages, defaults to app/Resources folder'
+ ),
+ new InputOption('format', 'f', InputOption::VALUE_OPTIONAL, 'Force the output format.', 'xlf'),
+ new InputOption('clean', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_NONE),
+ )
+ );
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->input = $input;
+ $this->output = $output;
+ $this->io = new SymfonyStyle($input, $output);
+ $kernel = $this->getContainer()->get('kernel');
+
+ $format = $this->input->getOption('format');
+ $clean = $this->input->getOption('clean');
+ $locale = $this->input->getArgument('locale');
+
+ $bundleName = $this->input->getArgument('bundle');
+ if ($bundleName) {
+ $bundle = $kernel->getBundle($bundleName);
+ $transPaths = $bundle->getPath() . '/Resources/translations';
+ }
+ $this->exportFile($transPaths, $locale, $format, $clean);
+ }
+
+ private function exportFile($transPaths, $locale, $format, $clean)
+ {
+ $db = $this->exportFromDB($locale);
+ $language = $this->getContainer()->get('doctrine')->getManager()->getRepository(Language::class)->findOneBy(
+ ['locale' => $locale]
+ );
+ $currentCatalogue = $this->extractMessages($locale, $transPaths);
+ $extractedCatalogue = new MessageCatalogue($locale);
+ if ($db != null) {
+ foreach ($db as $token) {
+ $translation = $token->getTranslation($language)->getTranslation();
+ if (!$translation) {
+ $translation = $token->getToken();
+ }
+ $extractedCatalogue->set($token->getToken(), $translation, $token->getCatalogue());
+ }
+ } else {
+ $this->output->writeln('No translations to export.');
+
+ return;
+ }
+ $writer = $this->getContainer()->get('translation.writer');
+ $supportedFormats = $writer->getFormats();
+ if (!in_array($format, $supportedFormats)) {
+ $this->io->error(
+ array('Wrong output format', 'Supported formats are: ' . implode(', ', $supportedFormats) . '.')
+ );
+
+ return 1;
+ }
+
+ $operation = $clean ? new TargetOperation($currentCatalogue, $extractedCatalogue) : new MergeOperation(
+ $currentCatalogue, $extractedCatalogue
+ );
+
+ $writer->writeTranslations(
+ $operation->getResult(),
+ $format,
+ array(
+ 'path' => $transPaths,
+ 'default_locale' => $this->getContainer()->getParameter('kernel.default_locale'),
+ )
+ );
+ }
+
+ /**
+ *
+ * @param string $locale
+ * @return TranslationToken[]
+ */
+ private function exportFromDB($locale)
+ {
+ $em = $this->getContainer()->get('doctrine')->getManager();
+
+ return $em->getRepository(TranslationToken::class)->getAllTokensByLocale($locale);
+ }
+
+ /**
+ *
+ * @param type $locale
+ * @param type $transPaths
+ * @return MessageCatalogue
+ */
+ private function extractMessages($locale, $transPaths)
+ {
+ /** @var TranslationLoader $loader */
+ $loader = $this->getContainer()->get('translation.loader');
+ $currentCatalogue = new MessageCatalogue($locale);
+
+ if (is_dir($transPaths)) {
+ $loader->loadMessages($transPaths, $currentCatalogue);
+ }
+
+ return $currentCatalogue;
+ }
+
+}
diff --git a/src/Command/ImportTranslationsCommand.php b/src/Command/ImportTranslationsCommand.php
new file mode 100644
index 0000000..2053a83
--- /dev/null
+++ b/src/Command/ImportTranslationsCommand.php
@@ -0,0 +1,155 @@
+setName('objectbg:translation:import')
+ ->setDefinition(
+ array(
+ new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
+ new InputArgument(
+ 'bundle',
+ InputArgument::OPTIONAL,
+ 'The bundle name or directory where to load the messages, defaults to app/Resources folder'
+ ),
+ new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'),
+ new InputOption('override', null, InputOption::VALUE_NONE, 'Should the update be done'),
+ )
+ )
+ ->setDescription('Displays translation messages information')
+ ->setHelp(
+ <<<'EOF'
+ The %command.name% command helps finding unused or missing translation
+messages and comparing them with the fallback ones by inspecting the
+templates and translation files of a given bundle or the app folder.
+You can display information about bundle translations in a specific locale:
+ php %command.full_name% en AcmeDemoBundle
+You can also specify a translation domain for the search:
+ php %command.full_name% --domain=messages en AcmeDemoBundle
+You can display information about app translations in a specific locale:
+ php %command.full_name% en
+You can display information about translations in all registered bundles in a specific locale:
+ php %command.full_name% --all en
+EOF
+ );
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->input = $input;
+ $this->output = $output;
+
+ $kernel = $this->getContainer()->get('kernel');
+ $transPaths = array($kernel->getRootDir() . '/Resources/');
+
+ $locale = $this->input->getArgument('locale');
+ $override = $this->input->getOption('override');
+
+
+ $bundleName = $this->input->getArgument('bundle');
+ if ($bundleName) {
+ $bundle = $kernel->getBundle($bundleName);
+ $transPaths[] = $bundle->getPath() . '/Resources/';
+ $transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName());
+ } elseif ($input->getOption('all')) {
+ foreach ($kernel->getBundles() as $bundle) {
+ $transPaths[] = $bundle->getPath() . '/Resources/';
+ $transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName());
+ }
+ }
+
+ $catalogue = $this->extractMessages($locale, $transPaths);
+ $this->importTranslationFiles($catalogue, $locale, $override);
+ }
+
+ /**
+ * @param string $locale
+ * @param array $transPaths
+ *
+ * @return MessageCatalogue
+ */
+ private function extractMessages($locale, $transPaths)
+ {
+ /** @var TranslationLoader $loader */
+ $loader = $this->getContainer()->get('translation.loader');
+
+ $currentCatalogue = new MessageCatalogue($locale);
+ foreach ($transPaths as $path) {
+ $path = $path . 'translations';
+ if (is_dir($path)) {
+ $loader->loadMessages($path, $currentCatalogue);
+ }
+ }
+
+ return $currentCatalogue;
+ }
+
+ public function importTranslationFiles(MessageCatalogue $messages, $locale, $override)
+ {
+ $domains = $messages->all();
+ $translationToken = null;
+ $translation = null;
+
+ $em = $this->getContainer()->get('doctrine')->getManager();
+ $language = $em->getRepository(Language::class)->findOneBy(['locale' => $locale]);
+
+ /** @var TranslationTokenRepository $transTokenRepo */
+ $transTokenRepo = $em->getRepository(TranslationToken::class);
+ /** @var TranslationRepository $transRepo */
+ $transRepo = $em->getRepository(Translation::class);
+
+ foreach ($domains as $catalogue => $messages) {
+ foreach ($messages as $token => $val) {
+ $translationToken = $transTokenRepo->findByTokenAndCatalogue($token, $catalogue);
+ if (!$translationToken) {
+ $translationToken = new TranslationToken();
+ $translationToken->setToken($token);
+ $translationToken->setCatalogue($catalogue);
+ } else {
+ $translation = $transRepo->getTranslationByTokenAndLanguage($translationToken, $language);
+ }
+
+ if (!$translation || $override) {
+ if (!$translation) {
+ $translation = new Translation();
+ }
+ $translation->setLanguage($language);
+ $translation->setTranslationToken($translationToken);
+ $translation->setTranslation($val);
+ $em->persist($translationToken);
+ $em->persist($translation);
+ }
+ }
+ }
+ $em->flush();
+ }
+
+}
diff --git a/src/Controller/CRUDController.php b/src/Controller/CRUDController.php
index 7dc39b1..55d4ed5 100644
--- a/src/Controller/CRUDController.php
+++ b/src/Controller/CRUDController.php
@@ -2,13 +2,16 @@
namespace ObjectBG\TranslationBundle\Controller;
+use ObjectBG\TranslationBundle\Entity\Translation;
+use Sonata\AdminBundle\Controller\CRUDController as BaseCRUDController;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
+use Symfony\Component\HttpFoundation\Request;
-class CRUDController extends \Sonata\AdminBundle\Controller\CRUDController
+class CRUDController extends BaseCRUDController
{
- public function listAction(\Symfony\Component\HttpFoundation\Request $request = null)
+ public function listAction(Request $request = null)
{
$canEdit = $this->admin->isGranted('EDIT');
$canView = $this->admin->isGranted('LIST');
@@ -66,14 +69,6 @@ public function listAction(\Symfony\Component\HttpFoundation\Request $request =
$tokens = $qb->getQuery()->getResult();
$formBuilder = $this->createFormBuilder();
-// $FormBuilder->add('tokens', 'collection', array(
-// 'type' => 'text',
-// 'label' => false,
-// 'allow_add' => true,
-// 'options' => array(
-// 'label' => false
-// )
-// ));
$formBuilder->add(
'translations',
@@ -151,7 +146,7 @@ function ($item) use ($token, $language) {
continue;
}
if (!$translation) {
- $translation = new \ObjectBG\TranslationBundle\Entity\Translation();
+ $translation = new Translation();
$translation->setLanguage($language);
$translation->setTranslationToken($token);
}
diff --git a/src/DependencyInjection/Compiler/OverrideTranslatorCompilerPass.php b/src/DependencyInjection/Compiler/OverrideTranslatorCompilerPass.php
index 6cdd920..024a0ee 100644
--- a/src/DependencyInjection/Compiler/OverrideTranslatorCompilerPass.php
+++ b/src/DependencyInjection/Compiler/OverrideTranslatorCompilerPass.php
@@ -7,7 +7,6 @@
class OverrideTranslatorCompilerPass implements CompilerPassInterface
{
-
/**
* {@inheritdoc}
*/
diff --git a/src/DependencyInjection/Compiler/TemplatingCompilerPass.php b/src/DependencyInjection/Compiler/TemplatingCompilerPass.php
index 23a678e..3a75a85 100644
--- a/src/DependencyInjection/Compiler/TemplatingCompilerPass.php
+++ b/src/DependencyInjection/Compiler/TemplatingCompilerPass.php
@@ -7,7 +7,6 @@
class TemplatingCompilerPass implements CompilerPassInterface
{
-
/**
* {@inheritdoc}
*/
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index fa4c6c5..226d048 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -12,7 +12,6 @@
*/
class Configuration implements ConfigurationInterface
{
-
/**
* {@inheritDoc}
*/
diff --git a/src/DependencyInjection/ObjectBGTranslationExtension.php b/src/DependencyInjection/ObjectBGTranslationExtension.php
index 4de78e8..a4ce7a2 100644
--- a/src/DependencyInjection/ObjectBGTranslationExtension.php
+++ b/src/DependencyInjection/ObjectBGTranslationExtension.php
@@ -14,7 +14,6 @@
*/
class ObjectBGTranslationExtension extends Extension
{
-
/**
* {@inheritDoc}
*/
@@ -23,7 +22,7 @@ public function load(array $configs, ContainerBuilder $container)
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
- $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.xml');
$loader->load('admins.xml');
}
diff --git a/src/Doctrine/Filter/LanguageFilter.php b/src/Doctrine/Filter/LanguageFilter.php
index 910f2e8..96d56b4 100644
--- a/src/Doctrine/Filter/LanguageFilter.php
+++ b/src/Doctrine/Filter/LanguageFilter.php
@@ -13,6 +13,6 @@ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAli
return "";
}
- return $targetTableAlias.'.locale = '.$this->getParameter('locale');
+ return $targetTableAlias . '.locale = ' . $this->getParameter('locale');
}
}
diff --git a/src/Entity/Language.php b/src/Entity/Language.php
index 49b4eb0..17f76b2 100644
--- a/src/Entity/Language.php
+++ b/src/Entity/Language.php
@@ -4,9 +4,10 @@
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
+use Symfony\Component\Validator\Constraints as Assert;
/**
- * @ORM\Entity(repositoryClass="ObjectBG\TranslationBundle\Repository\Language")
+ * @ORM\Entity(repositoryClass="ObjectBG\TranslationBundle\Entity\LanguageRepository")
* @ORM\Table(name="languages")
* @UniqueEntity(fields={"locale"}, message="This locale already exists")
* @UniqueEntity(fields={"name"}, message="This name already exists")
@@ -21,10 +22,17 @@ class Language
*/
private $id;
- /** @ORM\column(type="string", length=200, unique=true) */
+ /**
+ * @Assert\Locale()
+ * @Assert\NotBlank
+ * @ORM\Column(type="string", length=200, unique=true)
+ */
private $locale;
- /** @ORM\column(type="string", length=200, unique=true) */
+ /**
+ * @Assert\NotBlank
+ * @ORM\column(type="string", length=200, unique=true)
+ */
private $name;
public function getId()
diff --git a/src/Entity/LanguageRepository.php b/src/Entity/LanguageRepository.php
new file mode 100644
index 0000000..59f0f6d
--- /dev/null
+++ b/src/Entity/LanguageRepository.php
@@ -0,0 +1,10 @@
+getEntityManager();
+ $dql = "SELECT t FROM ObjectBGTranslationBundle:Translation t WHERE t.translationToken = :token AND t.language = :language";
+
+ $exists = $em->createQuery($dql)
+ ->setParameter('token', $translationToken)
+ ->setParameter('language', $language)
+ ->getOneOrNullResult();
+
+ return $exists;
+ }
}
diff --git a/src/Entity/TranslationToken.php b/src/Entity/TranslationToken.php
index fe114c9..9b67998 100644
--- a/src/Entity/TranslationToken.php
+++ b/src/Entity/TranslationToken.php
@@ -6,7 +6,7 @@
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity(repositoryClass="ObjectBG\TranslationBundle\Repository\TranslationToken")
+ * @ORM\Entity(repositoryClass="ObjectBG\TranslationBundle\Entity\TranslationTokenRepository")
* @ORM\Table(name="translation_tokens",
* uniqueConstraints={@ORM\UniqueConstraint(columns={"token", "catalogue"})}
* )
diff --git a/src/Entity/TranslationTokenRepository.php b/src/Entity/TranslationTokenRepository.php
new file mode 100644
index 0000000..aac5d3c
--- /dev/null
+++ b/src/Entity/TranslationTokenRepository.php
@@ -0,0 +1,47 @@
+getEntityManager();
+ $dql = 'SELECT COUNT(token) FROM ObjectBGTranslationBundle:TranslationToken token WHERE token.token = :token AND token.catalogue = :catalogue';
+
+
+ $exists = ((int)$em->createQuery($dql)
+ ->setParameter('token', $token)
+ ->setParameter('catalogue', $catalogue)
+ ->getSingleScalarResult()) > 0;
+
+ return $exists;
+ }
+
+ public function findByTokenAndCatalogue($token, $catalogue)
+ {
+ $em = $this->getEntityManager();
+ $dql = 'SELECT token FROM ObjectBGTranslationBundle:TranslationToken token WHERE token.token = :token AND token.catalogue = :catalogue';
+
+
+ $exists = $em->createQuery($dql)
+ ->setParameter('token', $token)
+ ->setParameter('catalogue', $catalogue)
+ ->getOneOrNullResult();
+
+ return $exists;
+ }
+
+ public function getAllTokensByLocale($locale)
+ {
+ $em = $this->getEntityManager();
+ $dql = 'SELECT token, translation FROM ObjectBGTranslationBundle:TranslationToken token JOIN token.translations translation JOIN translation.language language WITH language.locale = :locale';
+ $exists = $em->createQuery($dql)
+ ->setParameter('locale', $locale)
+ ->getResult();
+
+ return $exists;
+ }
+}
diff --git a/src/EventListener/CurrentTranslationLoader.php b/src/EventListener/CurrentTranslationLoader.php
index 8b63d51..0ef2737 100644
--- a/src/EventListener/CurrentTranslationLoader.php
+++ b/src/EventListener/CurrentTranslationLoader.php
@@ -3,6 +3,7 @@
namespace ObjectBG\TranslationBundle\EventListener;
use Doctrine\Common\EventSubscriber;
+use ObjectBG\TranslationBundle\Entity\Language;
use ObjectBG\TranslationBundle\TranslatableInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\PropertyAccess\PropertyAccess;
@@ -12,27 +13,29 @@ class CurrentTranslationLoader implements EventSubscriber
{
/**
- *
* @var Container
*/
- private $Container;
+ private $container;
/**
- *
* @var PropertyAccessor
*/
- private $PropertyAccess;
- private $Fallback = true;
+ private $propertyAccessor;
- public function __construct(Container $Container)
+ /**
+ * @var bool
+ */
+ private $fallback = true;
+
+ public function __construct(Container $container)
{
- $this->Container = $Container;
- $this->PropertyAccess = PropertyAccess::createPropertyAccessor();
+ $this->container = $container;
+ $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}
public function doFallback($trueFalse)
{
- $this->Fallback = $trueFalse;
+ $this->fallback = $trueFalse;
}
public function getSubscribedEvents()
@@ -52,19 +55,19 @@ public function postLoad($Event)
public function initializeCurrentTranslation($Entity)
{
- $TranslationService = $this->Container->get('object_bg.translation.service.translation');
- $CurrentLanguage = $TranslationService->getCurrentLanguage();
+ $translationService = $this->container->get('object_bg.translation.service.translation');
+ $CurrentLanguage = $translationService->getCurrentLanguage();
$success = $this->initializeTranslation($Entity, $CurrentLanguage);
- if ($success == false && $this->Fallback === true) {
+ if ($success == false && $this->fallback === true) {
$this->initializeFallbackTranslation($Entity);
}
}
private function initializeFallbackTranslation($Entity)
{
- $TranslationService = $this->Container->get('object_bg.translation.service.translation');
- $fallbacks = $TranslationService->getFallbackLocales();
+ $translationService = $this->container->get('object_bg.translation.service.translation');
+ $fallbacks = $translationService->getFallbackLocales();
foreach ($fallbacks as $fallback) {
if ($this->initializeTranslation($Entity, $fallback)) {
@@ -73,35 +76,38 @@ private function initializeFallbackTranslation($Entity)
}
}
- public function initializeTranslation($Entity, $Language)
+ public function initializeTranslation($entity, $languageOrLocale)
{
- if (!$Entity instanceof TranslatableInterface) {
+ if (!$entity instanceof TranslatableInterface) {
throw new \RuntimeException('Entity is not translatable');
}
- $TranslationService = $this->Container->get('object_bg.translation.service.translation');
+ $translationService = $this->container->get('object_bg.translation.service.translation');
- $Translations = $this->PropertyAccess->getValue($Entity, $TranslationService->getTranslationsField($Entity));
+ $translations = $this->propertyAccessor->getValue($entity, $translationService->getTranslationsField($entity));
- if (!$Translations) {
+ if (!$translations) {
return false;
}
- $PropertyAccess = $this->PropertyAccess;
+ $propertyAccessor = $this->propertyAccessor;
- $CurrentTranslation = $Translations->filter(
- function ($item) use ($TranslationService, $Language, $PropertyAccess) {
- $TranslationLanguage = $PropertyAccess->getValue($item, $TranslationService->getLanguageField($item));
+ $currentTranslation = $translations->filter(
+ function ($item) use ($translationService, $languageOrLocale, $propertyAccessor) {
+ $translationLanguage = $propertyAccessor->getValue($item, $translationService->getLanguageField($item));
- return $Language instanceof \ObjectBG\TranslationBundle\Entity\Language ? ($TranslationLanguage == $Language) : ($TranslationLanguage->getLocale(
- ) == $Language);
+ if ($languageOrLocale instanceof Language) {
+ return $translationLanguage == $languageOrLocale;
+ } else {
+ $translationLanguage->getLocale() == $languageOrLocale;
+ }
}
)->first();
- if (!$CurrentTranslation) {
+ if (!$currentTranslation) {
return false;
}
- $CurrentTranslationField = $TranslationService->getCurrentTranslationField($Entity);
- $this->PropertyAccess->setValue($Entity, $CurrentTranslationField, $CurrentTranslation);
+ $currentTranslationField = $translationService->getCurrentTranslationField($entity);
+ $this->propertyAccessor->setValue($entity, $currentTranslationField, $currentTranslation);
return true;
}
diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php
index 84fb357..3478940 100644
--- a/src/Exception/InvalidArgumentException.php
+++ b/src/Exception/InvalidArgumentException.php
@@ -4,14 +4,13 @@
class InvalidArgumentException extends \InvalidArgumentException
{
-
- public static function missingTranslations($TranslatableClass)
+ public static function missingTranslations($translatableClass)
{
- return new self('Missing translations association for entity '.$TranslatableClass);
+ return new self('Missing translations association for entity ' . $translatableClass);
}
- public static function missingRequiredAnnotation($Class, $Annotation)
+ public static function missingRequiredAnnotation($class, $annotation)
{
- return new self($Class.' is missing required annotation '.$Annotation);
+ return new self($class . ' is missing required annotation ' . $annotation);
}
}
diff --git a/src/Repository/Language.php b/src/Repository/Language.php
deleted file mode 100644
index ead824b..0000000
--- a/src/Repository/Language.php
+++ /dev/null
@@ -1,10 +0,0 @@
-
- ObjectBG\TranslationBundle\TranslationLoader
ObjectBG\TranslationBundle\Dumper\DatabaseDumper
diff --git a/src/TranslationLoader.php b/src/TranslationLoader.php
deleted file mode 100644
index 019d2fe..0000000
--- a/src/TranslationLoader.php
+++ /dev/null
@@ -1,42 +0,0 @@
-translationRepository = $entityManager->getRepository("ObjectBGTranslationBundle:Translation");
- $this->languageRepository = $entityManager->getRepository("ObjectBGTranslationBundle:Language");
- }
-
- public function load($resource, $locale, $domain = 'messages')
- {
- $catalogue = new MessageCatalogue($locale);
- $language = $this->languageRepository->findOneByLocale($locale);
-
- if ($language) {
- $translations = $this->translationRepository->getTranslations($language, $domain);
- foreach ($translations as $translation) {
- $catalogue->set(
- $translation->getTranslationToken()->getToken(),
- $translation->getTranslation(),
- $domain
- );
- }
- }
-
- return $catalogue;
- }
-}
diff --git a/src/TranslationService.php b/src/TranslationService.php
index a56eca1..94478c4 100644
--- a/src/TranslationService.php
+++ b/src/TranslationService.php
@@ -14,7 +14,6 @@
class TranslationService
{
-
private $typeGuesser;
/**
@@ -299,7 +298,7 @@ private function getFieldsList($options, $class)
// Check existing
foreach ($formFields as $field) {
if (!property_exists($class, $field)) {
- throw new \Exception("Field '".$field."' doesn't exist in ".$class);
+ throw new \Exception("Field '" . $field . "' doesn't exist in " . $class);
}
}