Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorsgiehl <stefan@piwik.org>2013-08-31 20:11:39 +0400
committersgiehl <stefan@piwik.org>2013-09-04 02:15:15 +0400
commitcf72bae506a807bee5fc304f23696bfef3de69fe (patch)
tree040978676a2be0bb7960ccb2774a695d2386c8d5 /core
parent53f0ec193131d0a30721f465179aa8737a720e98 (diff)
completely refactored TranslationWriter to Translate\Writer
- it's now able to filter/clean translations, which shouldn't be done in tests only - uses the new Translate\Filter and Translate\Validate classes - is now able to write the files, and not only return data and path to write
Diffstat (limited to 'core')
-rw-r--r--core/Translate/Writer.php357
-rw-r--r--core/TranslationWriter.php134
2 files changed, 357 insertions, 134 deletions
diff --git a/core/Translate/Writer.php b/core/Translate/Writer.php
new file mode 100644
index 0000000000..a1b295fa07
--- /dev/null
+++ b/core/Translate/Writer.php
@@ -0,0 +1,357 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ *
+ */
+namespace Piwik\Translate;
+
+use Exception;
+use Piwik\Common;
+use Piwik\PluginsManager;
+use Piwik\Translate\Filter\ByBaseTranslations;
+use Piwik\Translate\Filter\ByParameterCount;
+use Piwik\Translate\Filter\EncodedEntities;
+use Piwik\Translate\Filter\EmptyTranslations;
+use Piwik\Translate\Filter\UnnecassaryWhitespaces;
+use Piwik\Translate\Validate\CoreTranslations;
+use Piwik\Translate\Validate\NoScripts;
+
+/**
+ * Writes clean translations to file
+ *
+ * @package Piwik
+ * @package Piwik_Translate
+ */
+class Writer
+{
+ /**
+ * current language to write files for
+ *
+ * @var string
+ */
+ protected $_language = '';
+
+ /**
+ * Name of a plugin (if set in contructor)
+ *
+ * @var string|null
+ */
+ protected $_pluginName = null;
+
+ /**
+ * base translations (english) for the current instance
+ *
+ * @var array
+ */
+ protected $_baseTranslations = array();
+
+ /**
+ * translations to write to file
+ *
+ * @var array
+ */
+ protected $_translations = array();
+
+ /**
+ * Errors occured while cleaning the translations
+ *
+ * @var array
+ */
+ protected $_cleanErrors = array();
+
+ const __UNCLEANED__ = 'uncleaned';
+ const __CLEANED__ = 'cleaned';
+
+ protected $_currentState = self::__UNCLEANED__;
+
+ /**
+ * If $pluginName is given, Writer will be initialized for the given plugin if it exists
+ * Otherwise it will be initialized for core translations
+ *
+ * @param string $language ISO 639-1 alpha-2 language code
+ * @param string $pluginName optional plugin name
+ * @throws \Exception
+ */
+ public function __construct($language, $pluginName=null)
+ {
+ $this->setLanguage($language);
+
+ if (!empty($pluginName)) {
+ $installedPlugins = PluginsManager::getInstance()->readPluginsDirectory();
+
+ if (!in_array($pluginName, $installedPlugins)) {
+
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($pluginName)));
+ }
+
+ $this->_pluginName = $pluginName;
+ }
+
+ $this->_baseTranslations = $this->_loadTranslation('en');
+ $this->setTranslations($this->_loadTranslation($this->getLanguage()));
+ }
+
+ /**
+ * @param string $language ISO 639-1 alpha-2 language code
+ */
+ public function setLanguage($language)
+ {
+ $this->_language = $language;
+ }
+
+ /**
+ * @return string ISO 639-1 alpha-2 language code
+ */
+ public function getLanguage()
+ {
+ return $this->_language;
+ }
+
+ /**
+ * Returns if there are translations available or not
+ * @return bool
+ */
+ public function hasTranslations()
+ {
+ return !empty($this->_baseTranslations) && !empty($this->_translations);
+ }
+
+ /**
+ * Set the translations to write (and cleans them)
+ *
+ * @param $translations
+ */
+ public function setTranslations($translations)
+ {
+ $this->_currentState = self::__UNCLEANED__;
+ $this->_translations = $translations;
+ $this->_cleanTranslations();
+ }
+
+ /**
+ * Load translations from file
+ *
+ * @param string $lang ISO 639-1 alpha-2 language code
+ * @throws Exception
+ * @return array Array of translations ( plugin => ( key => translated string ) )
+ */
+ protected function _loadTranslation($lang)
+ {
+ $path = $this->_getTranslationPath('lang', $lang);
+ if (!is_readable($path)) {
+ return array();
+ }
+
+ $data = file_get_contents($path);
+ $translations = json_decode($data, true);
+ return $translations;
+ }
+
+ /**
+ * Returns the temporary path for translations
+ *
+ * @return string
+ */
+ public function getTemporaryTranslationPath()
+ {
+ return $this->_getTranslationPath('tmp');
+ }
+
+ /**
+ * Returns the path to translation files
+ *
+ * @return string
+ */
+ public function getTranslationPath()
+ {
+ return $this->_getTranslationPath('lang');
+ }
+
+ /**
+ * Get translation file path based on given params
+ *
+ * @param string $base Optional base directory (either 'lang' or 'tmp')
+ * @param string|null $lang forced language
+ * @throws \Exception
+ * @return string path
+ */
+ protected function _getTranslationPath($base, $lang=null)
+ {
+ if (empty($lang)) $lang = $this->getLanguage();
+
+ if (!Common::isValidFilename($lang) ||
+ ($base !== 'lang' && $base !== 'tmp')
+ ) {
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
+ }
+
+ if (!empty($this->_pluginName)) {
+
+ $installedPlugins = PluginsManager::getInstance()->readPluginsDirectory();
+
+ if (!in_array($this->_pluginName, $installedPlugins)) {
+
+ throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
+ }
+
+ if ($base == 'tmp') {
+ return sprintf('%s/tmp/plugins/%s/lang/%s.json', PIWIK_INCLUDE_PATH, $this->_pluginName, $lang);
+ } else {
+ return sprintf('%s/plugins/%s/lang/%s.json', PIWIK_INCLUDE_PATH, $this->_pluginName, $lang);
+ }
+ }
+
+ return sprintf('%s/%s/%s.json', PIWIK_INCLUDE_PATH, $base, $lang);
+ }
+
+
+ /**
+ * Converts translations to a string that can be written to a file
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ /*
+ * Use JSON_UNESCAPED_UNICODE and JSON_PRETTY_PRINT for PHP >= 5.4
+ */
+ $options = 0;
+ if (defined('JSON_UNESCAPED_UNICODE')) $options |= JSON_UNESCAPED_UNICODE;
+ if (defined('JSON_PRETTY_PRINT')) $options |= JSON_PRETTY_PRINT;
+
+ return json_encode($this->_translations, $options);
+ }
+
+ /**
+ * Save translations to file; translations should already be cleaned.
+ *
+ * @return bool|int False if failure, or number of bytes written
+ */
+ public function save()
+ {
+ $path = $this->getTranslationPath();
+
+ Common::mkdir(dirname($path));
+
+ return file_put_contents($path, $this->__toString());
+ }
+
+ /**
+ * Save translations to temporary file; translations should already be cleansed.
+ *
+ * @return bool|int False if failure, or number of bytes written
+ */
+ public function saveTemporary()
+ {
+ $path = $this->getTemporaryTranslationPath();
+
+ Common::mkdir(dirname($path));
+
+ return file_put_contents($path, $this->__toString());
+ }
+
+ /**
+ * Returns if the were translations removed while cleaning
+ *
+ * @return bool
+ */
+ public function hasErrors()
+ {
+ return !empty($this->_cleanErrors);
+ }
+
+ /**
+ * Returns the cleaning errors
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->_cleanErrors;
+ }
+
+ /**
+ * @throws \Exception
+ *
+ * @return bool error state
+ */
+ protected function _cleanTranslations()
+ {
+ // skip if already cleaned
+ if ($this->_currentState == self::__CLEANED__) {
+ return $this->hasErrors();
+ }
+
+ $this->_cleanErrors = array();
+
+ // skip if not translations available
+ if (!$this->hasTranslations()) {
+ $this->_currentState = self::__CLEANED__;
+ return false;
+ }
+
+ $basefilter = new ByBaseTranslations($this->_baseTranslations);
+ $cleanedTranslations = $basefilter->filter($this->_translations);
+ $filteredData = $basefilter->getFilteredData();
+ if (!empty($filteredData)) {
+ $this->_cleanErrors[] = "removed translations that are not present in base translations: " .var_export($filteredData, 1);
+ }
+
+ $emptyfilter = new EmptyTranslations($this->_baseTranslations);
+ $cleanedTranslations = $emptyfilter->filter($cleanedTranslations);
+ $filteredData = $emptyfilter->getFilteredData();
+ if (!empty($filteredData)) {
+ $this->_cleanErrors[] = "removed empty translations: " .var_export($filteredData, 1);
+ }
+
+ $parameterFilter = new ByParameterCount($this->_baseTranslations);
+ $cleanedTranslations = $parameterFilter->filter($cleanedTranslations);
+ $filteredData = $parameterFilter->getFilteredData();
+ if (!empty($filteredData)) {
+ $this->_cleanErrors[] = "removed translations that had diffrent parameter counts: " .var_export($filteredData, 1);
+ }
+
+ $whitespaceFilter = new UnnecassaryWhitespaces($this->_baseTranslations);
+ $cleanedTranslations = $whitespaceFilter->filter($cleanedTranslations);
+ $filteredData = $whitespaceFilter->getFilteredData();
+ if (!empty($filteredData)) {
+ $this->_cleanErrors[] = "filtered unnecassary whitespaces in some translations: " .var_export($filteredData, 1);
+ }
+
+ $entityFilter = new EncodedEntities($this->_baseTranslations);
+ $cleanedTranslations = $entityFilter->filter($cleanedTranslations);
+ $filteredData = $entityFilter->getFilteredData();
+ if (!empty($filteredData)) {
+ $this->_cleanErrors[] = "converting entities to characters in some translations: " .var_export($filteredData, 1);
+ }
+
+ $noscriptValidator = new NoScripts();
+ if (!$noscriptValidator->isValid($cleanedTranslations)) {
+ throw new Exception($noscriptValidator->getError());
+ }
+
+ // check requirements for core translations
+ if (empty($this->_pluginName)) {
+
+ $baseValidator = new CoreTranslations($this->_baseTranslations);
+ if(!$baseValidator->isValid($cleanedTranslations)) {
+ throw new Exception($baseValidator->getError());
+ }
+ }
+
+ $this->_currentState = self::__CLEANED__;
+
+ if ($cleanedTranslations != $this->_translations) {
+ $this->_cleanErrors[] = 'translations have been cleaned';
+ }
+
+ $this->_translations = $cleanedTranslations;
+ return $this->hasErrors();
+ }
+}
diff --git a/core/TranslationWriter.php b/core/TranslationWriter.php
deleted file mode 100644
index f627e601f7..0000000000
--- a/core/TranslationWriter.php
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- *
- */
-namespace Piwik;
-
-use Exception;
-use Piwik\Common;
-
-/**
- * Write translations to file
- *
- * @package Piwik
- */
-class TranslationWriter
-{
- static private $baseTranslation = null;
- static public $disableJsonOptions = false;
-
- /**
- * Clean a string that may contain HTML special chars, single/double quotes, HTML entities, leading/trailing whitespace
- *
- * @param string $s
- * @return string
- */
- static public function clean($s)
- {
- return html_entity_decode(trim($s), ENT_QUOTES, 'UTF-8');
- }
-
- /**
- * Quote a "C" string
- *
- * @param string $s
- * @return string
- */
- static public function quote($s)
- {
- return "'" . addcslashes($s, "'") . "'";
- }
-
- /**
- * Get translation file path
- *
- * @param string $lang ISO 639-1 alpha-2 language code
- * @param string $base Optional base directory (either 'lang' or 'tmp')
- * @throws Exception
- * @return string path
- */
- static public function getTranslationPath($lang, $base = 'lang')
- {
- if (!Common::isValidFilename($lang) ||
- ($base !== 'lang' && $base !== 'tmp')
- ) {
- throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
- }
-
- return PIWIK_INCLUDE_PATH . '/' . $base . '/' . $lang . '.json';
- }
-
- /**
- * Load translations from file
- *
- * @param string $lang ISO 639-1 alpha-2 language code
- * @throws Exception
- * @return array $translations Array of translations ( key => translated string )
- */
- static public function loadTranslation($lang)
- {
- $path = self::getTranslationPath($lang);
- if (!is_readable($path)) {
- throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($lang)));
- }
-
- $data = file_get_contents($path);
- $translations = json_decode($data, true);
- return $translations;
- }
-
- /**
- * Output translations to string
- *
- * @param array $translations multidimensional Array of translations ( plugin => array (key => translated string ) )
- * @return string
- */
- static public function outputTranslation($translations)
- {
- if (!self::$baseTranslation) {
- self::$baseTranslation = self::loadTranslation('en');
- }
- $en = self::$baseTranslation;
-
- $cleanedTranslations = array();
-
- // filter out all translations that don't exist in english translations
- foreach ($en AS $plugin => $enTranslations) {
- foreach ($enTranslations as $key => $en_translation) {
- if (isset($translations[$plugin][$key]) && !empty($translations[$plugin][$key])) {
- $cleanedTranslations[$plugin][$key] = $translations[$plugin][$key];
- }
- }
- }
-
- $options = 0;
- if (!self::$disableJsonOptions) {
- if (defined('JSON_UNESCAPED_UNICODE')) {
- $options |= JSON_UNESCAPED_UNICODE;
- }
- if (defined('JSON_PRETTY_PRINT')) {
- $options |= JSON_PRETTY_PRINT;
- }
- }
- return json_encode($cleanedTranslations, $options);
- }
-
- /**
- * Save translations to file; translations should already be cleansed.
- *
- * @param array $translations Array of translations ( key => translated string )
- * @param string $destinationPath Path of file to save translations to
- * @return bool|int False if failure, or number of bytes written
- */
- static public function saveTranslation($translations, $destinationPath)
- {
- return file_put_contents($destinationPath, self::outputTranslation($translations));
- }
-} \ No newline at end of file