00001 <?php
00012
00013 if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
00014 $IP = getenv( 'MW_INSTALL_PATH' );
00015 } else {
00016 $dir = __DIR__;
00017 $IP = "$dir/../../..";
00018 }
00019 require_once "$IP/maintenance/Maintenance.php";
00020
00021 class PoImport extends Maintenance {
00022 public function __construct() {
00023 parent::__construct();
00024 $this->mDescription = 'Po file importer (does not make changes unless specified).';
00025 $this->addOption(
00026 'file',
00027 'Gettext file to import (Translate specific formatting)',
00028 true,
00029 true
00030 );
00031 $this->addOption(
00032 'user',
00033 'User who makes edits to wiki',
00034 true,
00035 true
00036 );
00037 $this->addOption(
00038 'really',
00039 '(optional) Actually make changes',
00040 false,
00041 false
00042 );
00043 }
00044
00045 public function execute() {
00046
00047 $p = new PoImporter( $this->getOption( 'file' ) );
00048 $p->setProgressCallback( array( $this, 'myOutput' ) );
00049 list( $changes, $group ) = $p->parse();
00050
00051 if ( !count( $changes ) ) {
00052 $this->output( "No changes to import\n" );
00053 exit( 0 );
00054 }
00055
00056
00057 $w = new WikiWriter(
00058 $changes,
00059 $group,
00060 $this->getOption( 'user' ),
00061 !$this->hasOption( 'really' )
00062 );
00063
00064 $w->setProgressCallback( array( $this, 'myOutput' ) );
00065 $w->execute();
00066 }
00067
00075 public function myOutput( $text, $channel = null, $error = false ) {
00076 if ( $error ) {
00077 $this->error( $text, $channel );
00078 } else {
00079 $this->output( $text, $channel );
00080 }
00081 }
00082 }
00083
00088 class PoImporter {
00090 protected $progressCallback;
00091
00096 private $file = false;
00097
00101 public function __construct( $file ) {
00102 $this->file = $file;
00103 }
00104
00105 public function setProgressCallback( $callback ) {
00106 $this->progressCallback = $callback;
00107 }
00108
00110 protected function reportProgress( $text, $channel, $severity = 'status' ) {
00111 if ( is_callable( $this->progressCallback ) ) {
00112 $useErrorOutput = $severity === 'error';
00113 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
00114 }
00115 }
00116
00124 protected function initMessages( $id, $code ) {
00125 $group = MessageGroups::getGroup( $id );
00126
00127 $messages = $group->initCollection( $code );
00128 $messages->loadTranslations();
00129
00130 return $messages;
00131 }
00132
00137 public function parse() {
00138 $data = file_get_contents( $this->file );
00139 $data = str_replace( "\r\n", "\n", $data );
00140
00141 $matches = array();
00142 if ( preg_match( '/X-Language-Code:\s+(.*)\\\n/', $data, $matches ) ) {
00143 $code = $matches[1];
00144 $this->reportProgress( "Detected language as $code", 'code' );
00145 } else {
00146 $this->reportProgress( 'Unable to determine language code', 'code', 'error' );
00147
00148 return false;
00149 }
00150
00151 if ( preg_match( '/X-Message-Group:\s+(.*)\\\n/', $data, $matches ) ) {
00152 $groupId = $matches[1];
00153 $this->reportProgress( "Detected message group as $groupId", 'group' );
00154 } else {
00155 $this->reportProgress( 'Unable to determine message group', 'group', 'error' );
00156
00157 return false;
00158 }
00159
00160 $contents = $this->initMessages( $groupId, $code );
00161
00162 echo "----\n";
00163
00164 $poformat = '".*"\n?(^".*"$\n?)*';
00165 $quotePattern = '/(^"|"$\n?)/m';
00166
00167 $sections = preg_split( '/\n{2,}/', $data );
00168 $changes = array();
00169 foreach ( $sections as $section ) {
00170 $matches = array();
00171 if ( preg_match( "/^msgctxt\s($poformat)/mx", $section, $matches ) ) {
00172
00173 $key = preg_replace( $quotePattern, '', $matches[1] );
00174
00175
00176 if ( !isset( $contents[$key] ) ) {
00177 continue;
00178 }
00179 } else {
00180 continue;
00181 }
00182 $matches = array();
00183 if ( preg_match( "/^msgstr\s($poformat)/mx", $section, $matches ) ) {
00184
00185 $translation = preg_replace( $quotePattern, '', $matches[1] );
00186
00187 $translation = stripcslashes( $translation );
00188 } else {
00189 continue;
00190 }
00191
00192
00193 if ( preg_match( '/^#, fuzzy$/m', $section ) ) {
00194 $translation = TRANSLATE_FUZZY . $translation;
00195 }
00196
00197 $oldtranslation = (string)$contents[$key]->translation();
00198
00199 if ( $translation !== $oldtranslation ) {
00200 if ( $translation === '' ) {
00201 $this->reportProgress( "Skipping empty translation in the po file for $key!", 'empty' );
00202 } else {
00203 if ( $oldtranslation === '' ) {
00204 $this->reportProgress( "New translation for $key", 'new' );
00205 } else {
00206 $this->reportProgress( "Translation of $key differs:\n$translation", 'differs' );
00207 }
00208 $changes["$key/$code"] = $translation;
00209 }
00210 }
00211 }
00212
00213 return array( $changes, $groupId );
00214 }
00215 }
00216
00220 class WikiWriter {
00222 protected $progressCallback;
00223
00224 private $changes = array();
00225 private $dryrun = true;
00226 private $allclear = false;
00227 private $group = null;
00228 protected $user;
00229
00236 public function __construct( $changes, $groupId, $user, $dryrun = true ) {
00237 $this->changes = $changes;
00238 $this->group = MessageGroups::getGroup( $groupId );
00239 $this->user = User::newFromName( $user );
00240 $this->dryrun = $dryrun;
00241 }
00242
00243 public function setProgressCallback( $callback ) {
00244 $this->progressCallback = $callback;
00245 }
00246
00248 protected function reportProgress( $text, $channel, $severity = 'status' ) {
00249 if ( is_callable( $this->progressCallback ) ) {
00250 $useErrorOutput = $severity === 'error';
00251 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
00252 }
00253 }
00254
00258 public function execute() {
00259 if ( !$this->group ) {
00260 $this->reportProgress( "Group $groupId does not exist.", 'groupId', 'error' );
00261
00262 return;
00263 }
00264
00265 if ( !$this->user->idForName() ) {
00266 $this->reportProgress( "User $user does not exist.", 'user', 'error' );
00267
00268 return;
00269 }
00270
00271 $count = count( $this->changes );
00272 $this->reportProgress( "Going to update $count pages.", 'pagecount' );
00273
00274 $ns = $this->group->getNamespace();
00275
00276 foreach ( $this->changes as $title => $text ) {
00277 $this->updateMessage( $ns, $title, $text );
00278 }
00279 }
00280
00287 private function updateMessage( $namespace, $page, $text ) {
00288 $title = Title::makeTitleSafe( $namespace, $page );
00289
00290 if ( !$title instanceof Title ) {
00291 $this->reportProgress( 'INVALID TITLE!', $page, 'error' );
00292
00293 return;
00294 }
00295 $this->reportProgress( "Updating {$title->getPrefixedText()}... ", $title );
00296
00297 if ( $this->dryrun ) {
00298 $this->reportProgress( 'DRY RUN!', $title );
00299
00300 return;
00301 }
00302
00303 $article = new Article( $title, 0 );
00304
00305 $status = $article->doEdit(
00306 $text,
00307 'Updating translation from gettext import',
00308 0,
00309 false,
00310 $this->user
00311 );
00312
00313 if ( $status === true || ( is_object( $status ) && $status->isOK() ) ) {
00314 $this->reportProgress( 'OK!', $title );
00315 } else {
00316 $this->reportProgress( 'Failed!', $title );
00317 }
00318 }
00319 }
00320
00321 $maintClass = 'PoImport';
00322 require_once RUN_MAINTENANCE_IF_MAIN;