Author: david
Date: Thu Dec  8 09:28:49 2011
New Revision: 10378

Log:
Move i18n tasks to their own directory

Added:
   trunk/lib/task/i18n/
   trunk/lib/task/i18n/i18nConsolidateTask.class.php
      - copied unchanged from r10377, 
trunk/lib/task/i18nConsolidateTask.class.php
   trunk/lib/task/i18n/i18nDiffTask.class.php
      - copied unchanged from r10377, trunk/lib/task/i18nDiffTask.class.php
   trunk/lib/task/i18n/i18nRectifyTask.class.php
      - copied unchanged from r10377, trunk/lib/task/i18nRectifyTask.class.php
   trunk/lib/task/i18n/i18nRemoveDuplicatesTask.class.php
      - copied unchanged from r10377, 
trunk/lib/task/i18nRemoveDuplicatesTask.class.php
   trunk/lib/task/i18n/i18nUpdateFixturesTask.class.php
      - copied unchanged from r10377, 
trunk/lib/task/i18nUpdateFixturesTask.class.php
Deleted:
   trunk/lib/task/i18nConsolidateTask.class.php
   trunk/lib/task/i18nDiffTask.class.php
   trunk/lib/task/i18nRectifyTask.class.php
   trunk/lib/task/i18nRemoveDuplicatesTask.class.php
   trunk/lib/task/i18nUpdateFixturesTask.class.php

Copied: trunk/lib/task/i18n/i18nConsolidateTask.class.php (from r10377, 
trunk/lib/task/i18nConsolidateTask.class.php)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/lib/task/i18n/i18nConsolidateTask.class.php   Thu Dec  8 09:28:49 
2011        (r10378, copy of r10377, 
trunk/lib/task/i18nConsolidateTask.class.php)
@@ -0,0 +1,203 @@
+<?php
+
+/*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Restore i18n strings lost when XLIFF files were broken into plugin-specific
+ * directories
+ *
+ * @package    symfony
+ * @subpackage task
+ * @author     David Juhasz <[email protected]>
+ * @version    SVN: $Id$
+ */
+class i18nConsolidateTask extends sfBaseTask
+{
+  /**
+   * @see sfTask
+   */
+  protected function configure()
+  {
+    $this->addArguments(array(
+      new sfCommandArgument('culture', sfCommandArgument::REQUIRED, 'The 
target culture'),
+    ));
+
+    $this->addOptions(array(
+      // http://trac.symfony-project.org/ticket/8352
+      new sfCommandOption('application', null, 
sfCommandOption::PARAMETER_REQUIRED, 'The application name', true),
+    ));
+
+    $this->namespace = 'i18n';
+    $this->name = 'consolidate';
+    $this->briefDescription = 'Combine all application messages into a single 
output (XLIFF) file for ease of use by translators';
+
+    $this->detailedDescription = <<<EOF
+Combine all application messages into a single output (XLIFF) file for ease of 
use by translators.
+EOF;
+  }
+
+  /**
+   * @see sfTask
+   */
+  public function execute($arguments = array(), $options = array())
+  {
+    $this->logSection('i18n', sprintf('Consolidating *%s* i18n messages', 
$arguments['culture']));
+
+    // get i18n configuration from factories.yml
+    $config = 
sfFactoryConfigHandler::getConfiguration($this->configuration->getConfigPaths('config/factories.yml'));
+
+    $class = $config['i18n']['class'];
+    $params = $config['i18n']['param'];
+    unset($params['cache']);
+
+    // Extract i18n messages from php files (including plugins)
+    $i18n = new $class($this->configuration, new sfNoCache(), $params);
+    $extract = new sfI18nConsolidatedExtract($i18n, $arguments['culture']);
+    $extract->extract();
+
+    // Save to app/qubit/i18n/consolidated
+    $consolidated = new $class($this->configuration, new sfNoCache(), $params);
+    
$consolidated->setMessageSource(array(sfConfig::get('sf_web_dir').'/i18n/consolidated'),
 $arguments['culture']);
+    $consolidated->getMessageSource()->setCulture($arguments['culture']);
+    $extract->save($consolidated);
+  }
+}
+
+class sfI18nConsolidatedExtract extends sfI18nApplicationExtract
+{
+  protected $messageSource = array();
+
+  public function configure()
+  {
+    // Override sfI18nAcpplicationExtract::configure() so we extract from
+    // plugin XLIFF files
+  }
+
+  public function save($consolidated)
+  {
+    $messages = array();
+    $translates = array();
+
+    foreach ($this->i18n->getMessageSource()->read() as $catalogue => 
$translations)
+    {
+      foreach ($translations as $key => $values)
+      {
+        $messages[] = $key;
+
+        // build associative array containing translated values
+        if (!isset($translates[$key]) || 0 == strlen($translates[$key][0]))
+        {
+          $translates[$key] = $values;
+        }
+      }
+    }
+
+    // Sort and remove duplicates
+    $messages = array_unique($messages);
+    sort($messages);
+
+    // Add sources to XLIFF file
+    foreach ($messages as $message)
+    {
+      $consolidated->getMessageSource()->append($message);
+    }
+
+    // Save all sources to consolidated i18n file
+    $consolidated->getMessageSource()->save();
+
+    // Now save translated strings
+    foreach ($translates as $key => $item)
+    {
+      // Track source file for message in comments
+      $comment = $item[2];
+      if (isset($this->sourceFile[$key]))
+      {
+        $comment = $this->sourceFile[$key];
+      }
+
+      $consolidated->getMessageSource()->update($key, $item[0], $comment);
+    }
+  }
+
+  public function extract()
+  {
+    // Add global templates
+    $this->extractFromPhpFiles(sfConfig::get('sf_app_template_dir'));
+
+    // Add global librairies
+    $this->extractFromPhpFiles(sfConfig::get('sf_app_lib_dir'));
+
+    // Add forms
+    $this->extractFromPhpFiles(sfConfig::get('sf_lib_dir').'/form');
+
+    // Extract from modules
+    $modules = 
sfFinder::type('dir')->maxdepth(0)->in(sfConfig::get('sf_app_module_dir'));
+    foreach ($modules as $module)
+    {
+      $this->extractFromPhpFiles(array(
+        $module.'/actions',
+        $module.'/lib',
+        $module.'/templates',
+      ));
+    }
+
+    // Extract plugin strings
+    $plugins = 
sfFinder::type('dir')->maxdepth(0)->not_name('.')->in(sfConfig::get('sf_plugins_dir'));
+    foreach ($plugins as $plugin)
+    {
+      foreach (sfFinder::type('dir')->maxdepth(0)->in($plugin.'/modules') as 
$piModule)
+      {
+        $this->extractFromPhpFiles(array(
+          $piModule.'/actions',
+          $piModule.'/lib',
+          $piModule.'/templates',
+        ));
+      }
+    }
+  }
+
+  /**
+   * Extracts i18n strings from PHP files.
+   *
+   * @param string $dir The PHP full path name
+   */
+  protected function extractFromPhpFiles($dir)
+  {
+    $phpExtractor = new sfI18nPhpExtractor();
+
+    $files = sfFinder::type('file')->name('*.php');
+    $messages = array();
+    foreach ($files->in($dir) as $file)
+    {
+      $extracted = $phpExtractor->extract(file_get_contents($file));
+      $messages = array_merge($messages, $extracted);
+
+      // Track source file for all messages
+      foreach ($extracted as $message)
+      {
+        if (!isset($this->sourceFile[$message]))
+        {
+          // Link to file in googlecode repository
+          $this->sourceFile[$message] = 
str_replace(sfConfig::get('sf_web_dir'), 
'http://code.google.com/p/qubit-toolkit/source/browse/trunk', $file);
+        }
+      }
+    }
+
+    $this->updateMessages($messages);
+  }
+}

Copied: trunk/lib/task/i18n/i18nDiffTask.class.php (from r10377, 
trunk/lib/task/i18nDiffTask.class.php)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/lib/task/i18n/i18nDiffTask.class.php  Thu Dec  8 09:28:49 2011        
(r10378, copy of r10377, trunk/lib/task/i18nDiffTask.class.php)
@@ -0,0 +1,178 @@
+<?php
+
+/*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Output a list of removed and added i18n messages to allow auditing of
+ * changes, and preservation of translations data that may still be valid.
+ * Output formats currrently include csv and tab to allow easy import into a
+ * spreadsheet application.
+ *
+ * @package    symfony
+ * @subpackage task
+ * @author     David Juhasz <[email protected]>
+ * @version    SVN: $Id$
+ */
+class i18nDiffTask extends sfBaseTask
+{
+  const FORMAT_CSV = 'csv';
+  const FORMAT_TAB = 'tab';
+
+  /**
+   * @see sfTask
+   */
+  protected function configure()
+  {
+    $this->addArguments(array(
+      new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The 
application name'),
+      new sfCommandArgument('culture', sfCommandArgument::REQUIRED, 'The 
target culture'),
+    ));
+
+    $this->addOptions(array(
+      new sfCommandOption('file', 'f', sfCommandOption::PARAMETER_OPTIONAL, 
'Specify a destination filename for writing output', 'stdout'),
+      new sfCommandOption('format', 'o', sfCommandOption::PARAMETER_OPTIONAL, 
'Specify an output format (currently only supports csv & tab-delimited)', 
self::FORMAT_CSV)
+    ));
+
+    $this->namespace = 'i18n';
+    $this->name = 'diff';
+    $this->briefDescription = 'Compares existing XLIFF strings to new i18n 
strings extracted from PHP files as per the i18n:extract task.';
+
+    $this->detailedDescription = <<<EOF
+The [i18n:diff|INFO] task compares existing XLIFF strings to new i18n strings
+extracted from PHP files for the given application and target culture:
+
+  [./symfony i18n:diff frontend fr|INFO]
+
+By default, the task outputs to STDOUT. To specify an destination file
+use the [--file|COMMENT] or [-f|COMMENT] options:
+
+  [./symfony i18n:diff --file="french_diff.csv" frontend fr|INFO]
+  [./symfony i18n:diff -f="french_diff.csv" frontend fr|INFO]
+
+By defulat, the task outputs the differences in CSV format. To specify an
+alternate file format use the [--format|COMMENT] or [-t|COMMENT] options:
+
+  [./symfony i18n:diff --format="tab" frontend fr|INFO]
+  [./symfony i18n:diff -o="tab" frontend fr|INFO]
+
+Possible [--format|COMMENT] values are "csv" and "tab".
+EOF;
+  }
+
+  /**
+   * @see sfTask
+   * @see sfI18nExtract
+   */
+  public function execute($arguments = array(), $options = array())
+  {
+    $output = "";
+
+    if (strtolower($options['file']) != 'stdout')
+    {
+      $this->logSection('i18n', sprintf('Diff i18n strings for the "%s" 
application', $arguments['application']));
+    }
+
+    // get i18n configuration from factories.yml
+    $config = 
sfFactoryConfigHandler::getConfiguration($this->configuration->getConfigPaths('config/factories.yml'));
+
+    $class = $config['i18n']['class'];
+    $params = $config['i18n']['param'];
+    unset($params['cache']);
+
+    $this->i18n = new $class($this->configuration, new sfNoCache(), $params);
+    $extract = new sfI18nApplicationExtract($this->i18n, 
$arguments['culture']);
+    $extract->extract();
+
+    if (strtolower($options['file']) != 'stdout')
+    {
+      $this->logSection('i18n', sprintf('found "%d" new i18n strings', 
count($extract->getNewMessages())));
+      $this->logSection('i18n', sprintf('found "%d" old i18n strings', 
count($extract->getOldMessages())));
+    }
+
+    // Column headers
+    $rows[0] = array('Action', 'Source', 'Target');
+
+    // Old messages
+    foreach ($this->getOldTranslations($extract) as $source=>$target)
+    {
+      $rows[] = array('Removed', $source, $target);
+    }
+
+    // New messages
+    foreach ($extract->getNewMessages() as $message)
+    {
+      $rows[] = array('Added', $message);
+    }
+
+    // Choose output format
+    switch (strtolower($options['format']))
+    {
+      case 'csv':
+        foreach ($rows as $row)
+        {
+          $output .= '"'.implode('","', array_map('addslashes', $row))."\"\n";
+        }
+        break;
+      case 'tab':
+        foreach ($rows as $row)
+        {
+          $output .= implode("\t", $row)."\n";
+        }
+        break;
+    }
+
+    // Output file
+    if (strtolower($options['file']) != 'stdout')
+    {
+      echo "\n".$options['file'];
+      // Remove '=' if using -f="file.csv" notation
+      $filename = (substr($options['file'], 0, 1) == '=') ? 
substr($options['file'], 1) : $options['file'];
+      file_put_contents($filename, $output);
+    }
+    else
+    {
+      echo $output;
+    }
+  }
+
+  /**
+   * Loads old translations currently saved in the message sources.
+   *
+   * @param sfI18nApplicationExtract $extract
+   * @return array of source and target translations
+   */
+  public function getOldTranslations($extract)
+  {
+    $oldMessages = array_diff($extract->getCurrentMessages(), 
$extract->getAllSeenMessages());
+
+    foreach ($this->i18n->getMessageSource()->read() as $catalogue => 
$translations)
+    {
+      foreach ($translations as $key => $value)
+      {
+        $allTranslations[$key] = $value[0];
+      }
+    }
+
+    foreach ($oldMessages as $message)
+    {
+      $oldTranslations[$message] = $allTranslations[$message];
+    }
+
+    return $oldTranslations;
+  }
+}

Copied: trunk/lib/task/i18n/i18nRectifyTask.class.php (from r10377, 
trunk/lib/task/i18nRectifyTask.class.php)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/lib/task/i18n/i18nRectifyTask.class.php       Thu Dec  8 09:28:49 
2011        (r10378, copy of r10377, trunk/lib/task/i18nRectifyTask.class.php)
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Restore i18n strings lost when XLIFF files were broken into plugin-specific
+ * directories
+ *
+ * @package    symfony
+ * @subpackage task
+ * @author     David Juhasz <[email protected]>
+ * @version    SVN: $Id$
+ */
+class i18nRectifyTask extends sfBaseTask
+{
+  /**
+   * @see sfTask
+   */
+  protected function configure()
+  {
+    $this->addArguments(array(
+      new sfCommandArgument('culture', sfCommandArgument::REQUIRED, 'The 
target culture'),
+    ));
+
+    $this->addOptions(array(
+
+      // http://trac.symfony-project.org/ticket/8352
+      new sfCommandOption('application', null, 
sfCommandOption::PARAMETER_REQUIRED, 'The application name', true),
+    ));
+
+    $this->namespace = 'i18n';
+    $this->name = 'rectify';
+    $this->briefDescription = 'Copy i18n target messages from application 
source to plugin source. This prevents losing translated string in the 
fragmentation of application message source into multiple plugin message 
sources.';
+
+    $this->detailedDescription = <<<EOF
+FIXME
+EOF;
+  }
+
+  /**
+   * @see sfTask
+   */
+  public function execute($arguments = array(), $options = array())
+  {
+    $this->logSection('i18n', sprintf('Rectifying existing i18n strings for 
the "%s" application', $options['application']));
+
+    // get i18n configuration from factories.yml
+    $config = 
sfFactoryConfigHandler::getConfiguration($this->configuration->getConfigPaths('config/factories.yml'));
+
+    $class = $config['i18n']['class'];
+    $params = $config['i18n']['param'];
+    unset($params['cache']);
+
+    // Get current (saved) messages from ALL sources (app and plugin)
+    $this->i18n = new $class($this->configuration, new sfNoCache(), $params);
+    $this->i18n->getMessageSource()->setCulture($arguments['culture']);
+    $this->i18n->getMessageSource()->load();
+
+    $currentMessages = array();
+    foreach ($this->i18n->getMessageSource()->read() as $catalogue => 
$translations)
+    {
+      foreach ($translations as $key => $value)
+      {
+        // Use first message that has a valid translation
+        if (0 < strlen(trim($value[0])) && !isset($currentMessages[$key][0]))
+        {
+          $currentMessages[$key] = $value;
+        }
+      }
+    }
+
+    // Loop through plugins
+    $pluginNames = 
sfFinder::type('dir')->maxdepth(0)->relative()->not_name('.')->in(sfConfig::get('sf_plugins_dir'));
+    foreach ($pluginNames as $pluginName)
+    {
+      $this->logSection('i18n', sprintf('rectifying %s plugin strings', 
$pluginName));
+
+      $messageSource = 
sfMessageSource::factory($config['i18n']['param']['source'], 
sfConfig::get('sf_plugins_dir').'/'.$pluginName.'/i18n');
+      $messageSource->setCulture($arguments['culture']);
+      $messageSource->load();
+
+      // If the current plugin source *doesn't* have a translation, then try
+      // and get translated value from $currentMessages
+      foreach($messageSource->read() as $catalogue => $translations)
+      {
+        foreach ($translations as $key => &$value)
+        {
+          if (0 == strlen(trim($value[0])) && isset($currentMessages[$key]))
+          {
+            $messageSource->update($key, $currentMessages[$key][0], $value[2]);
+          }
+        }
+      }
+
+      $messageSource->save();
+    }
+  }
+}

Copied: trunk/lib/task/i18n/i18nRemoveDuplicatesTask.class.php (from r10377, 
trunk/lib/task/i18nRemoveDuplicatesTask.class.php)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/lib/task/i18n/i18nRemoveDuplicatesTask.class.php      Thu Dec  8 
09:28:49 2011        (r10378, copy of r10377, 
trunk/lib/task/i18nRemoveDuplicatesTask.class.php)
@@ -0,0 +1,127 @@
+<?php
+
+/*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Restore i18n strings lost when XLIFF files were broken into plugin-specific
+ * directories
+ *
+ * @package    symfony
+ * @subpackage task
+ * @author     David Juhasz <[email protected]>
+ * @version    SVN: $Id$
+ */
+class I18nRemoveDuplicatesTask extends sfBaseTask
+{
+  /**
+   * @see sfTask
+   */
+  protected function configure()
+  {
+    $this->addOptions(array(
+      // http://trac.symfony-project.org/ticket/8352
+      new sfCommandOption('application', null, 
sfCommandOption::PARAMETER_REQUIRED, 'The application name', true),
+      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 
'The environment', 'cli'),
+    ));
+
+    $this->namespace = 'i18n';
+    $this->name = 'remove-duplicates';
+    $this->briefDescription = 'Delete duplicate source messages';
+
+    $this->detailedDescription = <<<EOF
+FIXME
+EOF;
+  }
+
+  /**
+   * @see sfTask
+   */
+  public function execute($arguments = array(), $options = array())
+  {
+    $this->logSection('i18n', sprintf('Removing duplicate i18n sources for the 
"%s" application', $options['application']));
+
+    // Loop through plugins
+    $pluginNames = 
sfFinder::type('dir')->maxdepth(0)->relative()->not_name('.')->in(sfConfig::get('sf_plugins_dir'));
+    foreach ($pluginNames as $pluginName)
+    {
+      $this->logSection('i18n', sprintf('Removing %s duplicates', 
$pluginName));
+
+      foreach 
(sfFinder::type('files')->in(sfConfig::get('sf_plugins_dir').'/'.$pluginName.'/i18n')
 as $file)
+      {
+        self::deleteDuplicateSource($file);
+      }
+    }
+  }
+
+  public function deleteDuplicateSource($filename)
+  {
+    $modified = false;
+
+    // create a new dom, import the existing xml
+    $doc = new DOMDocument;
+    $doc->formatOutput = true;
+    $doc->preserveWhiteSpace = false;
+    $doc->load($filename);
+
+    $xpath = new DOMXPath($doc);
+
+    foreach ($xpath->query('//trans-unit') as $unit)
+    {
+      foreach ($xpath->query('./target', $unit) as $target)
+      {
+        break; // Only one target
+      }
+
+      foreach ($xpath->query('./source', $unit) as $source)
+      {
+        // If this is a duplicate source key, then delete it
+        if (isset($sourceStrings[$source->nodeValue]))
+        {
+          // If original target string is null, but *this* node has a valid
+          // translation
+          if (0 == strlen($sourceStrings[$source->nodeValue]->nodeValue) &&
+            0 < strlen($target->nodeValue))
+          {
+            // Copy this translated string to the trans-unit node we are 
keeping
+            $sourceStrings[$source->nodeValue]->nodeValue = $target->nodeValue;
+          }
+
+          // Remove duplicate
+          $unit->parentNode->removeChild($unit);
+          $modified = true;
+        }
+        else
+        {
+          $sourceStrings[$source->nodeValue] = $target;
+        }
+
+        break; // Only one source
+      }
+    }
+
+    // Update xliff file if modified
+    if ($modified)
+    {
+      $fileNode = $xpath->query('//file')->item(0);
+      $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
+
+      $doc->save($filename);
+    }
+  }
+
+}

Copied: trunk/lib/task/i18n/i18nUpdateFixturesTask.class.php (from r10377, 
trunk/lib/task/i18nUpdateFixturesTask.class.php)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/lib/task/i18n/i18nUpdateFixturesTask.class.php        Thu Dec  8 
09:28:49 2011        (r10378, copy of r10377, 
trunk/lib/task/i18nUpdateFixturesTask.class.php)
@@ -0,0 +1,179 @@
+<?php
+
+/*
+ * This file is part of Qubit Toolkit.
+ *
+ * Qubit Toolkit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Qubit Toolkit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Qubit Toolkit.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Restore i18n strings lost when XLIFF files were broken into plugin-specific
+ * directories
+ *
+ * @package    symfony
+ * @subpackage task
+ * @author     David Juhasz <[email protected]>
+ * @version    SVN: $Id$
+ */
+class I18nUpdateFixturesTask extends sfBaseTask
+{
+  /**
+   * @see sfTask
+   */
+  protected function configure()
+  {
+    $this->addArguments(array(
+      new sfCommandArgument('path', sfCommandArgument::REQUIRED, 'Path for 
xliff files'),
+    ));
+
+    $this->addOptions(array(
+      // http://trac.symfony-project.org/ticket/8352
+      new sfCommandOption('application', null, 
sfCommandOption::PARAMETER_REQUIRED, 'The application name', true),
+      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 
'The environment', 'cli'),
+      new sfCommandOption('filename', 'f', 
sfCommandOption::PARAMETER_OPTIONAL, 'Name of XLIFF files', 'fixtures.xml'),
+    ));
+
+    $this->namespace = 'i18n';
+    $this->name = 'update-fixtures';
+    $this->briefDescription = 'Reads XLIFF files from {{path}} and merges 
translations to database fixture files';
+
+    $this->detailedDescription = <<<EOF
+Reads XLIFF files from {{path}} and merges translations to database fixture 
files
+EOF;
+  }
+
+  /**
+   * @see sfTask
+   */
+  public function execute($arguments = array(), $options = array())
+  {
+    // Extract translation strings from XLIFF files
+    $translations = $this->extractTranslations($arguments, $options);
+
+    $this->updateFixtures($translations);
+  }
+
+  protected function extractTranslations($arguments = array(), $options = 
array())
+  {
+    $translations = array();
+
+    $this->logSection('i18n', sprintf('Find XLIFF files named "%s"', 
$options['filename']));
+
+    // Search for xliff files
+    $files = 
sfFinder::type('file')->name($options['filename'])->in($arguments['path']);
+
+    if (0 == count($files))
+    {
+      $this->logSection('i18n', 'No valid files found.  Please check path and 
filename');
+
+      return;
+    }
+
+    // Extract translation strings
+    foreach ($files as $file)
+    {
+      $culture = self::getTargetCulture($file);
+      $xliff = new sfMessageSource_XLIFF(substr($file, 0, strrpos('/')));
+
+      if (!($messages = $xliff->loadData($file)))
+      {
+        continue;
+      }
+
+      // Build list of translations, keyed on source value
+      foreach ($messages as $source => $message)
+      {
+        if (0 < strlen($message[0]))
+        {
+          $translations[$source][$culture] = $message[0];
+        }
+      }
+    }
+
+    return $translations;
+  }
+
+  protected function updateFixtures($translations)
+  {
+    $this->logSection('i18n', 'Writing new translations to fixtures...');
+
+    // Search for YAML files
+    $fixturesDirs = 
array_merge(array(sfConfig::get('sf_data_dir').'/fixtures'), 
$this->configuration->getPluginSubPaths('/data/fixtures'));
+    $files = sfFinder::type('file')->name('*.yml')->in($fixturesDirs);
+
+    if (0 == count($files))
+    {
+      $this->logSection('i18n', 'Error: Couldn\'t find any fixture files to 
write.');
+
+      return;
+    }
+
+    // Merge translations to YAML files in data/fixtures
+    foreach ($files as $file)
+    {
+      $modified = false;
+      $yaml = new sfYaml;
+      $fixtures = $yaml->load($file);
+
+      // Descend through fixtures hierarchy
+      foreach ($fixtures as $classname => &$fixture)
+      {
+        foreach ($fixture as $key => &$columns)
+        {
+          foreach ($columns as $column => &$value)
+          {
+            if (is_array($value) && isset($value['en']))
+            {
+              if (isset($translations[$value['en']]))
+              {
+                $value = array_merge($value, $translations[$value['en']]);
+
+                // Sort keys alphabetically
+                ksort($value);
+
+                $modified = true;
+              }
+            }
+          }
+        }
+      }
+
+      if ($modified)
+      {
+        $this->logSection('i18n', sprintf('Updating %s...', $file));
+
+        $contents = $yaml->dump($fixtures, 4);
+
+        if (0 < strlen($contents))
+        {
+          file_put_contents($file, $contents);
+        }
+      }
+    }
+
+    return $this;
+  }
+
+  protected static function getTargetCulture($filename)
+  {
+    libxml_use_internal_errors(true);
+    if (!$xml = simplexml_load_file($filename))
+    {
+      return;
+    }
+    libxml_use_internal_errors(false);
+
+    return (string) $xml->file['target-language'];
+  }
+}

-- 
You received this message because you are subscribed to the Google Groups 
"Qubit Toolkit Commits" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/qubit-commits?hl=en.

Reply via email to