public function execute()
 {
     if (!is_writable($this->getOption('target'))) {
         $this->error('Target directory is not writable.', 1);
     }
     $langs = TranslateUtils::parseLanguageCodes($this->getOption('lang', '*'));
     $group = MessageGroups::getGroup('core');
     $type = $this->getOption('type');
     foreach ($langs as $l) {
         $o = null;
         switch ($type) {
             case 'special':
                 $o = new SpecialPageAliasesCM($l);
                 break;
             case 'magic':
                 $o = new MagicWordsCM($l);
                 break;
             case 'namespace':
                 $o = new NamespaceCM($l);
                 break;
             default:
                 $this->error('Invalid type: Must be one of special, magic, namespace.', 1);
         }
         $export = $o->export('core');
         if ($export === '') {
             continue;
         }
         $matches = array();
         preg_match('~^(\\$[a-zA-Z]+)\\s*=~m', $export, $matches);
         if (!isset($matches[1])) {
             continue;
         }
         # remove useles comment
         $export = preg_replace("~^# .*\$\n~m", '', $export);
         if (strpos($export, '#!!') !== false) {
             $this->error("There are warnings with {$l}.");
         }
         $variable = preg_quote($matches[1], '~');
         /** @var FileBasedMessageGroup $group */
         $file = $group->getSourceFilePath($l);
         // bandage
         $real = Language::getFileName('/messages/Messages', $l);
         $file = preg_replace('~/i18n/(.+)\\.json$~', $real, $file);
         if (!file_exists($file)) {
             $this->error("File {$file} does not exist!");
             continue;
         }
         $data = file_get_contents($file);
         $export = trim($export) . "\n";
         $escExport = addcslashes($export, '\\$');
         # Darn backreferences
         $outFile = $this->getOption('target') . '/' . $group->getTargetFilename($l);
         $outFile = preg_replace('~/i18n/(.+)\\.json$~', $real, $outFile);
         $count = 0;
         $data = preg_replace("~{$variable}\\s*=.*?\n\\);\n~s", $escExport, $data, 1, $count);
         if ($count) {
             file_put_contents($outFile, $data);
         } else {
             $this->error("Adding new entry to {$outFile}, please double check location.");
             $pos = strpos($data, "*/");
             if ($pos === false) {
                 $this->error(". FAILED! Totally new file? No header?");
             } else {
                 $pos += 3;
             }
             $data = substr($data, 0, $pos) . "\n" . $export . substr($data, $pos);
             file_put_contents($outFile, $data);
         }
     }
 }
 public function execute()
 {
     $target = $this->getOption('target');
     if (!is_writable($target)) {
         $this->error("Target directory is not writable ({$target}).", 1);
     }
     $threshold = $this->getOption('threshold');
     $noFuzzy = $this->hasOption('no-fuzzy');
     $noLocation = '';
     if ($this->hasOption('no-location')) {
         $noLocation = '--no-location ';
     }
     $skip = array();
     if ($this->hasOption('skip')) {
         $skip = array_map('trim', explode(',', $this->getOption('skip')));
     }
     $reqLangs = TranslateUtils::parseLanguageCodes($this->getOption('lang'));
     $reqLangs = array_flip($reqLangs);
     foreach ($skip as $skipLang) {
         unset($reqLangs[$skipLang]);
     }
     $reqLangs = array_flip($reqLangs);
     $codemapOnly = $this->hasOption('codemaponly');
     $groupIds = explode(',', trim($this->getOption('group')));
     $groupIds = MessageGroups::expandWildcards($groupIds);
     $groups = MessageGroups::getGroupsById($groupIds);
     /** @var FileBasedMessageGroup $group */
     foreach ($groups as $groupId => $group) {
         if ($group->isMeta()) {
             $this->output("Skipping meta message group {$groupId}.\n");
             unset($groups[$groupId]);
             continue;
         }
         if (!$group instanceof FileBasedMessageGroup) {
             $this->output("EE2: Unexportable message group {$groupId}.\n");
             unset($groups[$groupId]);
             continue;
         }
     }
     if (!count($groups)) {
         $this->error("EE1: No valid message groups identified.", 1);
     }
     $changeFilter = false;
     $hours = $this->getOption('hours');
     if ($hours) {
         $namespaces = array();
         /** @var FileBasedMessageGroup $group */
         foreach ($groups as $group) {
             $namespaces[$group->getNamespace()] = true;
         }
         $namespaces = array_keys($namespaces);
         $bots = true;
         $changeFilter = array();
         $rows = TranslateUtils::translationChanges($hours, $bots, $namespaces);
         foreach ($rows as $row) {
             $title = Title::makeTitle($row->rc_namespace, $row->rc_title);
             $handle = new MessageHandle($title);
             $code = $handle->getCode();
             if (!$code) {
                 continue;
             }
             $groupIds = $handle->getGroupIds();
             foreach ($groupIds as $groupId) {
                 $changeFilter[$groupId][$code] = true;
             }
         }
     }
     $skipGroups = array();
     if ($this->hasOption('skipgroup')) {
         $skipGroups = array_map('trim', explode(',', $this->getOption('skipgroup')));
     }
     foreach ($groups as $groupId => $group) {
         if (in_array($groupId, $skipGroups)) {
             $this->output("Group {$groupId} is in skipgroup.\n");
             continue;
         }
         // No changes to this group at all
         if (is_array($changeFilter) && !isset($changeFilter[$groupId])) {
             $this->output("No recent changes to {$groupId}.\n");
             continue;
         }
         $langs = $reqLangs;
         if ($codemapOnly) {
             foreach ($langs as $index => $code) {
                 if ($group->mapCode($code) === $code) {
                     unset($langs[$index]);
                 }
             }
         }
         if ($threshold) {
             $stats = MessageGroupStats::forGroup($groupId);
             foreach ($langs as $index => $code) {
                 if (!isset($stats[$code])) {
                     unset($langs[$index]);
                     continue;
                 }
                 $total = $stats[$code][MessageGroupStats::TOTAL];
                 $translated = $stats[$code][MessageGroupStats::TRANSLATED];
                 if ($translated / $total * 100 < $threshold) {
                     unset($langs[$index]);
                 }
             }
         }
         // Filter out unchanged languages from requested languages
         if (is_array($changeFilter)) {
             $langs = array_intersect($langs, array_keys($changeFilter[$groupId]));
         }
         if (!count($langs)) {
             continue;
         }
         $this->output("Exporting {$groupId}...\n");
         $ffs = $group->getFFS();
         $ffs->setWritePath($target);
         $sourceLanguage = $group->getSourceLanguage();
         $collection = $group->initCollection($sourceLanguage);
         $definitionFile = false;
         if ($this->hasOption('ppgettext') && $ffs instanceof GettextFFS) {
             global $wgMaxShellMemory, $wgTranslateGroupRoot;
             // Need more shell memory for msgmerge.
             $wgMaxShellMemory = 402400;
             $path = $group->getSourceFilePath($sourceLanguage);
             $definitionFile = str_replace($wgTranslateGroupRoot, $this->getOption('ppgettext'), $path);
         }
         $whitelist = $group->getTranslatableLanguages();
         foreach ($langs as $lang) {
             // Do not export languges that are blacklisted (or not whitelisted).
             // Also check that whitelist is not null, which means that all
             // languages are allowed for translation and export.
             if (is_array($whitelist) && !isset($whitelist[$lang])) {
                 continue;
             }
             $collection->resetForNewLanguage($lang);
             $collection->loadTranslations();
             // Don't export ignored, unless it is the source language
             // or message documentation
             global $wgTranslateDocumentationLanguageCode;
             if ($lang !== $wgTranslateDocumentationLanguageCode && $lang !== $sourceLanguage) {
                 $collection->filter('ignored');
             }
             if ($noFuzzy) {
                 $collection->filter('fuzzy');
             }
             $ffs->write($collection);
             // Do post processing if requested.
             if ($definitionFile) {
                 if (is_file($definitionFile)) {
                     $targetFileName = $ffs->getWritePath() . "/" . $group->getTargetFilename($collection->code);
                     $cmd = "msgmerge --quiet " . $noLocation . "--output-file=" . $targetFileName . ' ' . $targetFileName . ' ' . $definitionFile;
                     wfShellExec($cmd, $ret);
                     // Report on errors.
                     if ($ret) {
                         $this->error("ERROR: {$ret}");
                     }
                 } else {
                     $this->error("{$definitionFile} does not exist.", 1);
                 }
             }
         }
     }
 }
 /**
  * Itterate through available languages, loading and parsing the data
  * message from the MediaWiki namespace and writing the data to its output
  * file handle.
  */
 protected function writeFiles()
 {
     $langs = TranslateUtils::parseLanguageCodes('*');
     unset($langs[array_search('en', $langs)]);
     $langs = array_merge(array('en'), $langs);
     foreach ($langs as $l) {
         // Load message page.
         switch ($this->type) {
             case 'special':
                 $title = Title::makeTitleSafe(NS_MEDIAWIKI, 'Sp-translate-data-SpecialPageAliases/' . $l);
                 break;
             case 'magic':
                 $title = Title::makeTitleSafe(NS_MEDIAWIKI, 'Sp-translate-data-MagicWords/' . $l);
                 break;
             default:
                 exit(1);
         }
         // Parse message page.
         if (!$title || !$title->exists()) {
             $this->output("Skiping {$l}...\n");
             $messagesNew = array();
         } else {
             $this->output("Processing {$l}...\n");
             $page = WikiPage::factory($title);
             $content = $page->getContent();
             $data = $content->getNativeData();
             // Parse message file.
             $segments = explode("\n", $data);
             array_shift($segments);
             array_shift($segments);
             unset($segments[count($segments) - 1]);
             unset($segments[count($segments) - 1]);
             $messagesNew = array();
             foreach ($segments as $segment) {
                 $parts = explode(' = ', $segment);
                 $key = array_shift($parts);
                 $translations = explode(', ', implode($parts));
                 $messagesNew[$key] = $translations;
             }
         }
         // Write data to handles.
         $namesEn = LanguageNames::getNames('en');
         $namesNative = Language::fetchLanguageNames();
         foreach ($this->handles as $group => $handle) {
             // Find messages to write to this handle.
             $messagesOut = array();
             if (!isset($this->messagesOld[$group])) {
                 continue;
             }
             foreach ($this->messagesOld[$group]['en'] as $key => $message) {
                 if (array_key_exists($key, $messagesNew)) {
                     $messagesOut[$key] = $messagesNew[$key];
                 } elseif (isset($this->messagesOld[$group][$l][$key])) {
                     $messagesOut[$key] = $this->messagesOld[$group][$l][$key];
                 }
             }
             // If there are messages to write, write them.
             if (count($messagesOut) > 0) {
                 $out = '';
                 switch ($this->type) {
                     case 'special':
                         $out .= "\n\n/** {$namesEn[$l]} ({$namesNative[$l]}) " . "*/\n\$specialPageAliases['{$l}'] = array(\n";
                         break;
                     case 'magic':
                         $out .= "\n\n/** {$namesEn[$l]} ({$namesNative[$l]}) *" . "/\n\$magicWords['{$l}'] = array(\n";
                         break;
                 }
                 foreach ($messagesOut as $key => $translations) {
                     foreach ($translations as $id => $translation) {
                         $translations[$id] = addslashes($translation);
                         if ($this->type === 'magic' && $translation == '0') {
                             unset($translations[$id]);
                         }
                     }
                     $translations = implode("', '", $translations);
                     switch ($this->type) {
                         case 'special':
                             $out .= "\t'{$key}' => array( '{$translations}' ),\n";
                             break;
                         case 'magic':
                             if ($this->messagesOld[$group]['en'][$key][0] === 0) {
                                 $out .= "\t'{$key}' => array( 0, '{$translations}' ),\n";
                             } else {
                                 $out .= "\t'{$key}' => array( '{$translations}' ),\n";
                             }
                             break;
                     }
                 }
                 $out .= ");";
                 fwrite($handle, $out);
             }
         }
     }
 }