From 6c58b30ca4c23f5682f87d1960232e8793373c4b Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Thu, 22 Feb 2018 10:48:28 +0000 Subject: [PATCH 1/3] [FEATURE] Added system xml (configuration) to enable nightly translation gathering cronjobs. Default is off, must be enabled to gather new translations on global scope every night. --- Cron/Collect.php | 184 +++++++++++++++++++++------------------ etc/adminhtml/system.xml | 20 ++++- i18n/en_US.csv | 4 + i18n/nl_NL.csv | 4 + 4 files changed, 126 insertions(+), 86 deletions(-) diff --git a/Cron/Collect.php b/Cron/Collect.php index 82e4acc..6275f35 100644 --- a/Cron/Collect.php +++ b/Cron/Collect.php @@ -1,84 +1,102 @@ -. - */ - -namespace Experius\MissingTranslations\Cron; - -/** - * Class Collect - * @package Experius\MissingTranslations\Cron - */ -class Collect -{ - /** - * @var \Experius\MissingTranslations\Model\Config\Source\Locale - */ - protected $localeSourceModel; - - /** - * @var \Experius\MissingTranslations\Model\TranslationCollector - */ - protected $translationCollector; - - public function __construct( - \Experius\MissingTranslations\Model\Config\Source\Locale $locale, - \Experius\MissingTranslations\Model\TranslationCollector $translationCollector - ) { - $this->localeSourceModel = $locale; - $this->translationCollector = $translationCollector; - } - - /** - * Executes collect cron, inserting all translations for active locales into the database on global scope - */ - public function existingTranslations() - { - /** Global scope only for now */ - $storeId = 0; - $locales = $this->localeSourceModel->getLocaleMapping(); - - foreach ($locales as $locale => $localeOptionArray) { - $this->translationCollector->updateTranslationDatabase( - $storeId, - $locale, - \Experius\MissingTranslations\Model\TranslationCollector::TRANSLATION_TYPE_EXISTING - ); - } - } - - /** - * Insert all missing translations found in missing translation files - * for active locales into the database on global scope - */ - public function missingTranslations() - { - /** Global scope only for now */ - $storeId = 0; - $locales = $this->localeSourceModel->getLocaleMapping(); - - foreach ($locales as $locale => $localeOptionArray) { - $this->translationCollector->updateTranslationDatabase( - $storeId, - $locale, - \Experius\MissingTranslations\Model\TranslationCollector::TRANSLATION_TYPE_MISSING - ); - } - } +. + */ + +namespace Experius\MissingTranslations\Cron; + +/** + * Class Collect + * @package Experius\MissingTranslations\Cron + */ +class Collect +{ + const XML_PATH_EXISTING_TRANSLATIONS_CRON = 'general/locale/cron_existing_translations'; + const XML_PATH_MISSING_TRANSLATIONS_CRON = 'general/locale/cron_missing_translations'; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * @var \Experius\MissingTranslations\Model\Config\Source\Locale + */ + protected $localeSourceModel; + + /** + * @var \Experius\MissingTranslations\Model\TranslationCollector + */ + protected $translationCollector; + + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Experius\MissingTranslations\Model\Config\Source\Locale $locale, + \Experius\MissingTranslations\Model\TranslationCollector $translationCollector + ) { + $this->scopeConfig = $scopeConfig; + $this->localeSourceModel = $locale; + $this->translationCollector = $translationCollector; + } + + /** + * Executes collect cron, inserting all translations for active locales into the database on global scope + */ + public function existingTranslations() + { + if (!$this->scopeConfig->isSetFlag(self::XML_PATH_EXISTING_TRANSLATIONS_CRON)) { + return; + } + + /** Global scope only for now */ + $storeId = 0; + $locales = $this->localeSourceModel->getLocaleMapping(); + + foreach ($locales as $locale => $localeOptionArray) { + $this->translationCollector->updateTranslationDatabase( + $storeId, + $locale, + \Experius\MissingTranslations\Model\TranslationCollector::TRANSLATION_TYPE_EXISTING + ); + } + } + + /** + * Insert all missing translations found in missing translation files + * for active locales into the database on global scope + */ + public function missingTranslations() + { + if (!$this->scopeConfig->isSetFlag(self::XML_PATH_MISSING_TRANSLATIONS_CRON)) { + return; + } + + /** Global scope only for now */ + $storeId = 0; + $locales = $this->localeSourceModel->getLocaleMapping(); + + foreach ($locales as $locale => $localeOptionArray) { + $this->translationCollector->updateTranslationDatabase( + $storeId, + $locale, + \Experius\MissingTranslations\Model\TranslationCollector::TRANSLATION_TYPE_MISSING + ); + } + } } \ No newline at end of file diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5d91acf..f5ec3d5 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -1,10 +1,24 @@ - +
- - + + + + + + Magento\Config\Model\Config\Source\Yesno + If enabled all newly found existing translations are inserted into the database every night + + + + Magento\Config\Model\Config\Source\Yesno + If enabled all newly found missing translations are inserted into the database every night
diff --git a/i18n/en_US.csv b/i18n/en_US.csv index a480143..6f05c9b 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -1,5 +1,9 @@ "Has a translation", "Has a translation" +Missing translations cron enabled,Missing translations cron enabled "Database Translations", "Database Translations" +Enable to gather all newly found existing translations into the database every night,Enable to gather all newly found existing translations into the database every night +Enable to gather all newly found missing translations into the database every night,Enable to gather all newly found missing translations into the database every night +Existing translations cron enabled,Existing translations cron enabled "Translate Missing String", "Translate Missing String" "Columns", "Columns" "Original String", "Original String" diff --git a/i18n/nl_NL.csv b/i18n/nl_NL.csv index bbdd083..a9a6cdb 100644 --- a/i18n/nl_NL.csv +++ b/i18n/nl_NL.csv @@ -1,5 +1,9 @@ "Has a translation", "Heeft een vertaling" +Missing translations cron enabled,Missende verstalingen crontaak ingeschakeld "Database Translations", "Database vertalingen" +Enable to gather all newly found existing translations into the database every night,"Schakel in om elke nacht alle nieuw gevonden, bestaande vertalingen in de database te zetten" +Enable to gather all newly found missing translations into the database every night,"Schakel in om elke nacht alle nieuw gevonden, missende vertalingen in de database te zetten" +Existing translations cron enabled,Bestaande vertalingen crontaak ingeschakeld "Translate Missing String", "Vertaal missende vertaling" "Columns", "Kolommen" "Original String", "Originele String" From d40be483b91aaec41592829ecaeb81f02fcb7577 Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Thu, 22 Feb 2018 10:49:37 +0000 Subject: [PATCH 2/3] [FEATURE] Added update function of all js-translation.json files. This function is triggered for all themes with a specific locale, on saving a translation in the missing translations section. This enables real-time updates of javascript translations for each locale active in a Magento 2 installation. --- .../Adminhtml/Translation/InlineEdit.php | 23 +- Controller/Adminhtml/Translation/Save.php | 19 +- Helper/Data.php | 293 +++++++++++------- 3 files changed, 217 insertions(+), 118 deletions(-) diff --git a/Controller/Adminhtml/Translation/InlineEdit.php b/Controller/Adminhtml/Translation/InlineEdit.php index f91fa60..f2027aa 100755 --- a/Controller/Adminhtml/Translation/InlineEdit.php +++ b/Controller/Adminhtml/Translation/InlineEdit.php @@ -27,21 +27,37 @@ */ class InlineEdit extends \Magento\Backend\App\Action { - + /** + * @var \Magento\Framework\Controller\Result\JsonFactory + */ protected $jsonFactory; /** + * @var \Experius\MissingTranslations\Model\TranslationFactory + */ + protected $translationFactory; + + /** + * @var \Experius\MissingTranslations\Helper\Data + */ + protected $helper; + /** + * InlineEdit constructor. * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory + * @param \Experius\MissingTranslations\Model\TranslationFactory $translationFactory + * @param \Experius\MissingTranslations\Helper\Data $helper */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\Controller\Result\JsonFactory $jsonFactory, - \Experius\MissingTranslations\Model\TranslationFactory $translationFactory + \Experius\MissingTranslations\Model\TranslationFactory $translationFactory, + \Experius\MissingTranslations\Helper\Data $helper ) { parent::__construct($context); $this->jsonFactory = $jsonFactory; $this->translationFactory = $translationFactory; + $this->helper = $helper; } /** @@ -75,6 +91,9 @@ public function execute() try { $model->setData(array_merge($data, $postItems[$modelId])); $model->save(); + + $this->helper->updateJsTranslationJsonFiles($data['locale']); + } catch (\Exception $e) { $messages[] = "[Translation ID: {$modelId}] {$e->getMessage()}"; $error = true; diff --git a/Controller/Adminhtml/Translation/Save.php b/Controller/Adminhtml/Translation/Save.php index 27be87a..7430a35 100644 --- a/Controller/Adminhtml/Translation/Save.php +++ b/Controller/Adminhtml/Translation/Save.php @@ -53,23 +53,38 @@ class Save extends \Magento\Backend\App\Action */ protected $phrases; + /** + * @var \Magento\Translation\Model\Inline\CacheManager + */ + protected $cacheManager; + + /** + * @var \Magento\Framework\Locale\ResolverInterface + */ + protected $localeResolver; + /** * Save constructor. * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor + * @param \Experius\MissingTranslations\Model\TranslationFactory $translationFactory * @param \Experius\MissingTranslations\Helper\Data $helper + * @param \Magento\Backend\Model\Auth\Session $authSession + * @param \Magento\Framework\Locale\ResolverInterface $localeResolver */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor, \Experius\MissingTranslations\Model\TranslationFactory $translationFactory, \Experius\MissingTranslations\Helper\Data $helper, - \Magento\Backend\Model\Auth\Session $authSession + \Magento\Backend\Model\Auth\Session $authSession, + \Magento\Framework\Locale\ResolverInterface $localeResolver ) { $this->dataPersistor = $dataPersistor; $this->translationFactory = $translationFactory; $this->helper = $helper; $this->authSession = $authSession; + $this->localeResolver = $localeResolver; parent::__construct($context); } @@ -129,6 +144,8 @@ public function execute() $this->messageManager->addSuccess(__('You saved the Translation.')); $this->dataPersistor->clear('experius_missingtranslations_translation'); + $this->helper->updateJsTranslationJsonFiles($data['locale']); + if ($this->getRequest()->getParam('back')) { return $resultRedirect->setPath('*/*/edit', ['key_id' => $model->getId()]); } diff --git a/Helper/Data.php b/Helper/Data.php index 7cbe0c2..3a9e31d 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -1,116 +1,179 @@ -. - */ - -namespace Experius\MissingTranslations\Helper; - -use Magento\Framework\DataObject; - -/** - * Class Data - * @package Experius\MissingTranslations\Helper - */ -class Data extends \Magento\Framework\App\Helper\AbstractHelper -{ - /** - * @var \Magento\Framework\App\Filesystem\DirectoryList - */ - protected $directory_list; - - /** - * @var array - */ - protected $phrases = []; - - /** - * Data constructor. - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Framework\App\Filesystem\DirectoryList $directory_list - */ - public function __construct( - \Magento\Framework\App\Helper\Context $context, - \Magento\Framework\App\Filesystem\DirectoryList $directory_list - ) - { - $this->directory_list = $directory_list; - - parent::__construct($context); - } - - /** - * Get language vendor from configuration for current store - * - * @return string - */ - public function getLanguageVendor() - { - return $this->scopeConfig->getValue('general/locale/language_vendor', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - } - - /** - * Get translation phrases from missing translation files (if generated) - * - * @return array - */ - public function getPhrases($locale = 'en_US') - { - $this->phrases = array(); - $filename = $this->getFileName($locale); - if ($filename) { - $this->phrases = array_map('str_getcsv', file($filename)); - } - return $this->phrases; - } - - /** - * Remove translation line from missing translation file - * - * @param bool $line - * @param string $locale - */ - public function removeFromFile($line = false, $locale = 'en_US') - { - if ($line) { - $filename = $this->getFileName($locale); - if ($filename) { - $lines = file($filename); - unset($lines[$line]); - // write the new data to the file - $fp = fopen($filename, 'w'); - fwrite($fp, implode('', $lines)); - fclose($fp); - } - } - } - - /** - * Get filename of missing translation file based of locale - * - * @param string $locale - * @return bool|string - */ - public function getFileName($locale = 'en_US') - { - $vendor = $this->getLanguageVendor(); - $filename = $this->directory_list->getRoot() . '/app/i18n/'. $vendor . '/missing/' . $locale . '.csv'; - - return (file_exists($filename)) ? $filename : false; - } +. + */ + +namespace Experius\MissingTranslations\Helper; + +use Magento\Framework\DataObject; + +/** + * Class Data + * @package Experius\MissingTranslations\Helper + */ +class Data extends \Magento\Framework\App\Helper\AbstractHelper +{ + /** + * @var array + */ + protected $phrases = []; + + /** + * @var \Magento\Framework\App\Filesystem\DirectoryList + */ + protected $directoryList; + + /** + * @var \Magento\Framework\Filesystem\Driver\File + */ + protected $driverFile; + + /** + * @var \Magento\Framework\Translate\ResourceInterface + */ + protected $translateResource; + + /** + * @var \Magento\Framework\App\View\Deployment\Version\StorageInterface + */ + protected $versionStorage; + + /** + * @var \Magento\Framework\View\Design\Theme\ThemePackageList + */ + protected $themePackageList; + + /** + * Data constructor. + * @param \Magento\Framework\App\Helper\Context $context + * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList + * @param \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage + */ + public function __construct( + \Magento\Framework\App\Helper\Context $context, + \Magento\Framework\App\Filesystem\DirectoryList $directoryList, + \Magento\Framework\Filesystem\Driver\File $driverFile, + \Magento\Framework\Translate\ResourceInterface $translateResource, + \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage, + \Magento\Framework\View\Design\Theme\ThemePackageList $themePackageList + ) { + $this->directoryList = $directoryList; + $this->driverFile = $driverFile; + $this->translateResource = $translateResource; + $this->versionStorage = $versionStorage; + $this->themePackageList = $themePackageList; + + parent::__construct($context); + } + + /** + * Update js-translation.json files in static content for specific locale + */ + public function updateJsTranslationJsonFiles($locale = null) + { + if (!$locale) { + return; + } + + $translations = $this->translateResource->getTranslationArray(null, $locale); + $translationsJson = json_encode($translations); + + $themes = $this->themePackageList->getThemes(); + + foreach ($themes as $relativePath => $theme) { + $jsonFilePath = $this->directoryList->getPath(\Magento\Framework\App\Filesystem\DirectoryList::STATIC_VIEW) . + \DIRECTORY_SEPARATOR . + $relativePath . + \DIRECTORY_SEPARATOR . + $locale . + \DIRECTORY_SEPARATOR . + \Magento\Translation\Model\Js\Config::DICTIONARY_FILE_NAME; + $this->driverFile->filePutContents($jsonFilePath, $translationsJson); + } + } + + /** + * Updated static content version number + */ + public function updateStaticVersionNumber() + { + $version = (new \DateTime())->getTimestamp(); + $this->versionStorage->save($version); + } + + /** + * Get language vendor from configuration for current store + * + * @return string + */ + public function getLanguageVendor() + { + return $this->scopeConfig->getValue('general/locale/language_vendor', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + } + + /** + * Get translation phrases from missing translation files (if generated) + * + * @return array + */ + public function getPhrases($locale = 'en_US') + { + $this->phrases = array(); + $filename = $this->getFileName($locale); + if ($filename) { + $this->phrases = array_map('str_getcsv', file($filename)); + } + return $this->phrases; + } + + /** + * Remove translation line from missing translation file + * + * @param bool $line + * @param string $locale + */ + public function removeFromFile($line = false, $locale = 'en_US') + { + if ($line) { + $filename = $this->getFileName($locale); + if ($filename) { + $lines = file($filename); + unset($lines[$line]); + // write the new data to the file + $fp = fopen($filename, 'w'); + fwrite($fp, implode('', $lines)); + fclose($fp); + } + } + } + + /** + * Get filename of missing translation file based of locale + * + * @param string $locale + * @return bool|string + */ + public function getFileName($locale = 'en_US') + { + $vendor = $this->getLanguageVendor(); + $filename = $this->directoryList->getRoot() . '/app/i18n/'. $vendor . '/missing/' . $locale . '.csv'; + + return (file_exists($filename)) ? $filename : false; + } } \ No newline at end of file From f7aad8e57c112e462029b136aca668f79760f24b Mon Sep 17 00:00:00 2001 From: Boris van Katwijk Date: Fri, 23 Feb 2018 15:47:20 +0000 Subject: [PATCH 3/3] [BUGFIX] If file exists was not being checked in putting js-translation.json file on translation save. Deployed version number was not being updated resulting in translations not coming through in the frontend with all cache one (full_page). --- Helper/Data.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Helper/Data.php b/Helper/Data.php index 3a9e31d..427c95d 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -96,6 +96,7 @@ public function updateJsTranslationJsonFiles($locale = null) $themes = $this->themePackageList->getThemes(); + $staticVersionUpdateRequired = false; foreach ($themes as $relativePath => $theme) { $jsonFilePath = $this->directoryList->getPath(\Magento\Framework\App\Filesystem\DirectoryList::STATIC_VIEW) . \DIRECTORY_SEPARATOR . @@ -104,7 +105,14 @@ public function updateJsTranslationJsonFiles($locale = null) $locale . \DIRECTORY_SEPARATOR . \Magento\Translation\Model\Js\Config::DICTIONARY_FILE_NAME; - $this->driverFile->filePutContents($jsonFilePath, $translationsJson); + if ($this->driverFile->isExists($jsonFilePath)) { + $this->driverFile->filePutContents($jsonFilePath, $translationsJson); + $staticVersionUpdateRequired = true; + } + } + + if ($staticVersionUpdateRequired) { + $this->updateStaticVersionNumber(); } }