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); } } } }