MediaWiki  REL1_23
generateJsonI18n.php
Go to the documentation of this file.
00001 <?php
00002 
00028 require_once __DIR__ . '/Maintenance.php';
00029 
00035 class GenerateJsonI18n extends Maintenance {
00036     public function __construct() {
00037         parent::__construct();
00038         $this->mDescription = "Build JSON messages files from a PHP messages file";
00039         $this->addArg( 'phpfile', 'PHP file defining a $messages array', true );
00040         $this->addArg( 'jsondir', 'Directory to write JSON files to. ' .
00041             'Required unless <phpfile> exists and --shim-only is specified', false );
00042         $this->addOption( 'langcode', 'Language code; only needed for converting core i18n files',
00043             false, true );
00044         $this->addOption( 'shim-only', 'Only create or update the backward-compatibility shim' );
00045     }
00046 
00047     public function execute() {
00048         $phpfile = $this->getArg( 0 );
00049         $jsondir = $this->getArg( 1 );
00050 
00051         if ( $this->hasOption( 'shim-only' ) ) {
00052             $this->shimOnly( $phpfile, $jsondir );
00053             return;
00054         }
00055 
00056         if ( $jsondir === null ) {
00057             $this->error( 'Argument [jsondir] is required unless --shim-only is specified.' );
00058             $this->maybeHelp( true );
00059         }
00060 
00061         if ( !is_readable( $phpfile ) ) {
00062             $this->error( "Error reading $phpfile\n", 1 );
00063         }
00064         include $phpfile;
00065         $phpfileContents = file_get_contents( $phpfile );
00066 
00067         if ( !isset( $messages ) ) {
00068             $this->error( "PHP file $phpfile does not define \$messages array\n", 1 );
00069         }
00070 
00071         $extensionStyle = true;
00072         if ( !isset( $messages['en'] ) || !is_array( $messages['en'] ) ) {
00073             if ( !$this->hasOption( 'langcode' ) ) {
00074                 $this->error( "PHP file $phpfile does not set language codes, --langcode " .
00075                     "is required.\n", 1 );
00076             }
00077             $extensionStyle = false;
00078             $langcode = $this->getOption( 'langcode' );
00079             $messages = array( $langcode => $messages );
00080         } elseif ( $this->hasOption( 'langcode' ) ) {
00081             $this->output( "Warning: --langcode option set but will not be used.\n" );
00082         }
00083 
00084         foreach ( $messages as $langcode => $langmsgs ) {
00085             $authors = $this->getAuthorsFromComment( $this->findCommentBefore(
00086                 $extensionStyle ? "\$messages['$langcode'] =" : '$messages =',
00087                 $phpfileContents
00088             ) );
00089             // Make sure the @metadata key is the first key in the output
00090             $langmsgs = array_merge(
00091                 array( '@metadata' => array( 'authors' => $authors ) ),
00092                 $langmsgs
00093             );
00094 
00095             $jsonfile = "$jsondir/$langcode.json";
00096             $success = file_put_contents(
00097                 $jsonfile,
00098                 FormatJson::encode( $langmsgs, "\t", FormatJson::ALL_OK ) . "\n"
00099             );
00100             if ( $success === false ) {
00101                 $this->error( "FAILED to write $jsonfile", 1 );
00102             }
00103             $this->output( "$jsonfile\n" );
00104         }
00105 
00106         if ( !$this->hasOption( 'langcode' ) ) {
00107             $shim = $this->doShim( $jsondir );
00108             file_put_contents( $phpfile, $shim );
00109         }
00110 
00111         $this->output( "All done.\n" );
00112         $this->output( "Also add \$wgMessagesDirs['YourExtension'] = __DIR__ . '/i18n';\n" );
00113     }
00114 
00115     protected function shimOnly( $phpfile, $jsondir ) {
00116         if ( file_exists( $phpfile ) ) {
00117             if ( !is_readable( $phpfile ) ) {
00118                 $this->error( "Error reading $phpfile\n", 1 );
00119             }
00120 
00121             $phpfileContents = file_get_contents( $phpfile );
00122             $m = array();
00123             if ( !preg_match( '!"/([^"$]+)/\$csCode.json";!', $phpfileContents, $m ) ) {
00124                 $this->error( "Cannot recognize $phpfile as a shim.\n", 1 );
00125             }
00126 
00127             if ( $jsondir === null ) {
00128                 $jsondir = $m[1];
00129             }
00130 
00131             $this->output( "Updating existing shim $phpfile\n" );
00132         } elseif ( $jsondir === null ) {
00133             $this->error( "$phpfile does not exist.\n" .
00134                 "Argument [jsondir] is required in order to create a new shim.\n", 1 );
00135         } else {
00136             $this->output( "Creating new shim $phpfile\n" );
00137         }
00138 
00139         $shim = $this->doShim( $jsondir );
00140         file_put_contents( $phpfile, $shim );
00141         $this->output( "All done.\n" );
00142     }
00143 
00144     protected function doShim( $jsondir ) {
00145         $shim = <<<'PHP'
00146 <?php
00158 $messages = array();
00159 if ( !function_exists( '{{FUNC}}' ) ) {
00160     function {{FUNC}}( $cache, $code, &$cachedData ) {
00161         $codeSequence = array_merge( array( $code ), $cachedData['fallbackSequence'] );
00162         foreach ( $codeSequence as $csCode ) {
00163             $fileName = dirname( __FILE__ ) . "/{{OUT}}/$csCode.json";
00164             if ( is_readable( $fileName ) ) {
00165                 $data = FormatJson::decode( file_get_contents( $fileName ), true );
00166                 foreach ( array_keys( $data ) as $key ) {
00167                     if ( $key === '' || $key[0] === '@' ) {
00168                         unset( $data[$key] );
00169                     }
00170                 }
00171                 $cachedData['messages'] = array_merge( $data, $cachedData['messages'] );
00172             }
00173 
00174             $cachedData['deps'][] = new FileDependency( $fileName );
00175         }
00176         return true;
00177     }
00178 
00179     $GLOBALS['wgHooks']['LocalisationCacheRecache'][] = '{{FUNC}}';
00180 }
00181 
00182 PHP;
00183 
00184         $jsondir = str_replace( '\\', '/', $jsondir );
00185         $shim = str_replace( '{{OUT}}', $jsondir, $shim );
00186         $shim = str_replace( '{{FUNC}}', 'wfJsonI18nShim' . wfRandomString( 16 ), $shim );
00187         return $shim;
00188     }
00189 
00196     protected function findCommentBefore( $needle, $haystack ) {
00197         $needlePos = strpos( $haystack, $needle );
00198         if ( $needlePos === false ) {
00199             return '';
00200         }
00201         // Need to pass a negative offset to strrpos() so it'll search backwards from the
00202         // offset
00203         $startPos = strrpos( $haystack, '
00216