Making WordPress.org

Ticket #760: 760-wporg.4.diff

File 760-wporg.4.diff, 33.3 KB (added by stephdau, 10 years ago)
  • extend/plugins-plugins/svn-track/class.dotorg-plugins-i18n.php

     
     1<?php
     2class Dotorg_Plugin_I18n {
     3        var $db;
     4        var $tracker;
     5
     6        // TODO: remove when we launch for all plugins.
     7        var $translated_plugins = array(
     8                'blogware-importer',
     9                'livejournal-importer',
     10                'dotclear-importer',
     11        );
     12
     13        var $i18n_cache_group = 'plugin-i18n';
     14
     15        function __construct( $db, $tracker = null ) {
     16                if ( !empty( $db ) && is_object( $db ) )
     17                        $this->db = $db;
     18                if ( !empty( $tracker ) && is_object( $tracker ) )
     19                        $this->tracker = $tracker;
     20                wp_cache_add_global_groups( $this->i18n_cache_group );
     21        }
     22
     23        /*
     24         * ***********************
     25         * Processing
     26         * ***********************
     27         */
     28
     29        function process( $slug, $branch = 'dev', $type = 'all' ) {
     30                if ( empty( $slug ) || empty( $this->tracker ) )
     31                        return false;
     32
     33                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     34                // TODO: remove when we launch for all plugins.
     35                if ( ! in_array( $slug, $this->translated_plugins ) )
     36                        return false;
     37
     38                if ( 'stable' !== $branch )
     39                        $branch = 'dev';
     40
     41                if ( 'code' !== $type && 'readme' !== $type )
     42                        $type = 'all';
     43
     44                $path_rel = "{$slug}/trunk/";
     45
     46                if ( 'stable' === $branch ) {
     47                        if ( false == ( $stable_tag = $this->tracker->get_stable_tag_dir_using( $path_rel ) ) ) {
     48                                // Can't get a stable tag, bail out
     49                                return false;
     50                        } else if ( 'trunk' == trim( $stable_tag['tag_dir'], '/' ) ) {
     51                                // If stable is trunk, then it's really same as dev, switch to that
     52                                $branch = 'dev';
     53                        } else {
     54                                // We're dealing with an actual stable tag, go for it
     55                                $path_rel = "{$slug}/{$stable_tag['tag_dir']}"; //
     56                        }
     57                }
     58
     59                $this->set_glotpress_for_plugin( $slug );
     60
     61                if ( 'code' === $type || 'all' === $type )
     62                        $this->process_code( $path_rel, $branch );
     63
     64                if ( 'readme' === $type || 'all' === $type )
     65                        $this->process_readme( $path_rel, $branch );
     66
     67                echo "Processed {$type} for {$path_rel}\n";
     68                return true;
     69        }
     70
     71        function process_code( $path_rel, $branch = 'dev' ) {
     72                if ( empty( $this->tracker ) )
     73                        return false;
     74
     75                $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel );
     76
     77                if ( empty( $slug ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) )
     78                        return false;
     79
     80                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     81                // TODO: remove when we launch for all plugins.
     82                if ( !in_array( $slug, $this->translated_plugins ) )
     83                        return false;
     84
     85                $export_path = $this->tracker->create_export( $path_rel );
     86
     87                if ( empty( $export_path ) || !is_dir( $export_path ) )
     88                        return false;
     89
     90                $old_cwd = getcwd();
     91                chdir( $export_path );
     92
     93                // Check for a plugin text domain declaration and loading, grep recursively, not necessarily in [slug].php
     94                if ( ! shell_exec( 'grep -r --include "*.php" "Text Domain: ' . escapeshellarg( $slug ) . '" .' ) && ! shell_exec( 'grep -r --include "*.php" "\bload_plugin_textdomain\b" .' ) )
     95                        return false;
     96
     97                if ( !class_exists( 'PotExtMeta' ) )
     98                        require_once( __DIR__ . '/i18n-tools/pot-ext-meta.php' );
     99
     100                // Create pot file from code
     101                $pot_file = "./tmp-{$slug}.pot"; // Using tmp- prefix in case a plugin has $slug.pot committed
     102                $makepot  = new MakePOT;
     103
     104                if ( ! $makepot->wp_plugin( '.', $pot_file, $slug ) || ! file_exists( $pot_file ) )
     105                        return false;
     106
     107                // DEBUG
     108                // system( "cat {$pot_file}" );
     109
     110                $this->import_to_glotpress_project( $slug, $branch, $pot_file );
     111
     112                chdir( $old_cwd );
     113                return true;
     114        }
     115
     116        function process_readme( $path_rel, $branch = 'dev' ) {
     117                if ( empty( $this->tracker ) )
     118                        return false;
     119
     120                $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel );
     121
     122                if ( empty( $slug ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) )
     123                        return false;
     124
     125                // DEBUG: in_array as separate check because we'll start the program with a finite list of plugins
     126                // TODO: remove when we launch for all plugins.
     127                if ( !in_array( $slug, $this->translated_plugins ) )
     128                        return false;
     129
     130                $export_path = $this->tracker->create_export( $path_rel );
     131
     132                if ( empty( $export_path ) || !is_dir( $export_path ) )
     133                        return false;
     134
     135                $old_cwd = getcwd();
     136                chdir( $export_path );
     137
     138                $readme = $this->tracker->parse_readme_in( $path_rel );
     139
     140                $str_priorities = array();
     141
     142                if ( !class_exists( 'PO' ) )
     143                        require_once( __DIR__ . '/i18n-tools/pomo/po.php' );
     144
     145                $pot = new PO;
     146
     147                // No need for license, being in the directory implies GPLv2 or later. Add here otherwise.
     148                foreach ( array( 'name', 'short_description' ) as $key ) {
     149                        $readme[ $key ] = trim( $readme[ $key ] ) ;
     150                }
     151
     152                // If empty or "sketchy", get the plugin name form the PHP file's headers
     153                if ( empty( $readme['name'] ) || 'Plugin Name' == trim( $readme['name'] ) ) {
     154                        // -o in grep will make sure we don't get comments opening delimiters (//, /*) or spaces as part of string
     155                        $name_from_php  = trim( shell_exec( 'grep -o "\bPlugin Name:.*" ' . escapeshellarg( $slug ) . '.php' ) );
     156                        // Remove the header label
     157                        $name_from_php  = str_replace( 'Plugin Name:', '', $name_from_php );
     158                        // Do clean out potential comment closing delimiter (*/) out of string
     159                        $name_from_php  = preg_replace( '|^(.+)[\s]+?\*/$|', '\1', $name_from_php );
     160                        // Use trimmed results as plugin name
     161                        $readme['name'] = trim( $name_from_php );
     162                }
     163
     164                if ( !empty( $readme['name'] ) ) {
     165                        $pot->add_entry( new Translation_Entry ( array(
     166                                'singular'           => $readme['name'],
     167                                'extracted_comments' => 'Name.',
     168                        ) ) );
     169
     170                        $str_priorities[ $readme['name'] ] = 1;
     171                }
     172
     173                if ( !empty( $readme['short_description'] ) ) {
     174                        $pot->add_entry( new Translation_Entry ( array(
     175                                'singular'           => $readme['short_description'],
     176                                'extracted_comments' => 'Short description.',
     177                        ) ) );
     178
     179                        $str_priorities[ $readme['short_description'] ] = 1;
     180                }
     181
     182                if ( !empty( $readme['screenshots'] ) ) {
     183                        foreach ( $readme['screenshots'] as $sshot_key => $sshot_desc ) {
     184                                $sshot_desc = trim( $sshot_desc );
     185                                $pot->add_entry( new Translation_Entry ( array(
     186                                        'singular'           => $sshot_desc,
     187                                        'extracted_comments' => 'Screenshot description.',
     188                                ) ) );
     189                        }
     190
     191                }
     192
     193                if ( empty( $readme['sections'] ) )
     194                        $readme['sections'] = array();
     195
     196                // Adding remaining content as a section so it's processed by the same loop below
     197                if ( !empty( $readme['remaining_content'] ) )
     198                        $readme['sections']['remaining_content'] = $readme['remaining_content'];
     199
     200                $strings = array();
     201
     202                foreach ( $readme['sections'] as $section_key => $section_text ) {
     203                        if ( 'screenshots' == $section_key )
     204                                continue;
     205
     206                        /*
     207                         * Scanned tags based on block elements found in Automattic_Readme::filter_text() $allowed.
     208                         * Scanning H3/4, li, p and blockquote. Other tags are ignored  in strings (a, strong, cite, etc).
     209                         * Processing notes:
     210                         * * Don't normalize/modify original text, will be used as a search pattern in original doc in some use-cases.
     211                         * * Using regexes over XML parsing for performance reasons, could move to the latter for more accuracy.
     212                         */
     213
     214                        if ( 'changelog' !== $section_key ) { // No need to scan non-translatable version headers in changelog
     215                                if ( preg_match_all( '|<h[3-4]+[^>]*>(.+)</h[3-4]+>|', $section_text, $matches ) ) {
     216                                        if ( !empty( $matches[1] ) ) {
     217                                                foreach ( $matches[1] as $text ) {
     218                                                        $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} header" );
     219                                                }
     220                                        }
     221                                }
     222                        }
     223
     224                        if ( preg_match_all( '|<li>(.+)</li>|', $section_text, $matches ) ) {
     225                                if ( !empty( $matches[1] ) ) {
     226                                        foreach ( $matches[1] as $text ) {
     227                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} list item" );
     228                                                if ( 'changelog' === $section_key )
     229                                                        $str_priorities[ $text ] = -1;
     230                                        }
     231                                }
     232                        }
     233
     234                        if ( preg_match_all( '|<p>(.+)</p>|', $section_text, $matches ) ) {
     235                                if ( !empty( $matches[1] ) ) {
     236                                        foreach ( $matches[1] as $text ) {
     237                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} paragraph" );
     238                                                if ( 'changelog' === $section_key )
     239                                                        $str_priorities[ $text ] = -1;
     240                                        }
     241                                }
     242                        }
     243
     244                        if ( preg_match_all( '|<blockquote>(.+)</blockquote>|', $section_text, $matches ) ) {
     245                                if ( !empty( $matches[1] ) ) {
     246                                        foreach ( $matches[1] as $text ) {
     247                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} block quote" );
     248                                                if ( 'changelog' === $section_key )
     249                                                        $str_priorities[ $text ] = -1;
     250                                        }
     251                                }
     252                        }
     253                }
     254
     255                foreach ( $strings as $text => $comments ) {
     256                        $pot->add_entry( new Translation_Entry ( array(
     257                                'singular'           => $text,
     258                                'extracted_comments' => 'Found in ' . implode( $comments, ", " ) . '.',
     259                        ) ) );
     260                }
     261
     262                $pot_file = "./tmp-{$slug}-readme.pot";
     263                $pot->export_to_file( $pot_file );
     264
     265                // DEBUG
     266                // system( "cat {$pot_file}" );
     267
     268                // import to GlotPress, dev or stable
     269                $this->import_to_glotpress_project( $slug, "{$branch}-readme", $pot_file, $str_priorities );
     270
     271                chdir( $old_cwd );
     272                return true;
     273        }
     274
     275        function handle_translator_comment( $array, $key, $val ) {
     276                $val = trim( preg_replace( '/[^a-z0-9]/i', ' ', $val ) ); // cleanup key names for display
     277                if ( empty( $array[ $key ] ) ) {
     278                        $array[ $key ] = array( $val );
     279                } else if ( !in_array( $val, $array[ $key ] ) ) {
     280                        $array[ $key ][] = $val;
     281                }
     282                return $array;
     283        }
     284
     285        function import_to_glotpress_project( $project, $branch, $file, $str_priorities = array() ) {
     286                if ( empty( $project ) || empty( $branch ) || empty( $file ) )
     287                        return;
     288                // Note: this will only work if the GlotPress project/sub-projects exist.
     289                $cmd = 'php ' . __DIR__ . '/../../../translate/glotpress/scripts/import-originals.php -o po -p ' . escapeshellarg( "wp-plugins/{$project }/{$branch}" ) . ' -f ' . escapeshellarg( $file );
     290                // DEBUG
     291                // var_dump( $cmd );
     292                system( $cmd );
     293                if ( empty( $str_priorities ) )
     294                        return;
     295                $branch_id = $this->get_gp_branch_id( $project, $branch );
     296                // Set the string priorities in GP once the originals have been imported
     297                if ( empty( $branch_id ) )
     298                        return;
     299                foreach ( (array) $str_priorities as $str => $prio ) {
     300                        if ( 1 !== $prio && -1 !== $prio )
     301                                $prio = 0;
     302                        $res = $this->db->query( $this->db->prepare(
     303                                'UPDATE translate_originals SET priority = %d WHERE project_id = %d AND status = %s AND singular = %s',
     304                                $prio, $branch_id, '+active', $str
     305                        ) );
     306                }
     307        }
     308
     309        function set_glotpress_for_plugin( $plugin_slug ) {
     310                if ( empty( $plugin_slug ) )
     311                        return;
     312                $cmd = 'php ' . __DIR__ . '/../../../translate/bin/set-wp-plugin-project.php ' . escapeshellarg( $plugin_slug );
     313                // DEBUG
     314                // var_dump( $cmd );
     315                system( $cmd );
     316        }
     317
     318        /*
     319         * ***********************
     320         * Rendering
     321         * ***********************
     322         */
     323
     324        function translate( $key, $content, $args = array() ) {
     325                if ( empty( $key ) || empty( $content ) )
     326                        return $content;
     327
     328                if ( !empty( $args['topic_id'] ) && is_numeric( $args['topic_id'] ) )
     329                        $topic = get_topic( $args['topic_id'] );
     330                else
     331                        global $topic;
     332
     333                if ( empty( $topic ) )
     334                        return $content;
     335
     336                $language     = '';
     337                $server_name  = strtolower( $_SERVER['SERVER_NAME'] );
     338                if ( 'api.wordpress.org' == $server_name ) {
     339                        if ( preg_match( '/^[a-z]{2}(_[A-Z]{2})?$/', trim( $_REQUEST['locale'] ) ) ) {
     340                                $language = substr( trim( $_REQUEST['locale'] ), 0, 5 );
     341                        } else if ( !empty( $_REQUEST['request'] ) ) {
     342                                $request = maybe_unserialize( $_REQUEST['request'] );
     343                                if ( !empty( $request ) && !empty( $request->locale ) && preg_match( '/^[a-z]{2}(_[A-Z]{2})?$/', trim( $request->locale ) )  ) {
     344                                        $language = trim( $request->locale );
     345                                }
     346                        }
     347                } else if ( preg_match( '/^([^\.]+)\.wordpress\.org$/', $server_name, $matches ) ) {
     348                        $subdomain = $this->verify_subdomain( $matches[1] );
     349                        if ( ! empty( $subdomain ) )
     350                                $language = substr( $server_name, 0, 2 );
     351                }
     352
     353                if ( empty( $language ) || 'en' === $language || 'en_US' === $language )
     354                        return $content;
     355
     356                $slug = $topic->plugin_san;
     357
     358                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     359                // TODO: remove when we launch for all plugins.
     360                if ( empty( $slug ) || ! in_array( $slug, $this->translated_plugins ) )
     361                        return $content;
     362
     363                $branch = ( empty( $topic->stable_tag ) || 'trunk' === $topic->stable_tag ) ? 'dev' : 'stable';
     364
     365                if ( empty( $args['code_i18n'] ) || true !== $args['code_i18n'] )
     366                        $branch .= '-readme';
     367
     368                $cache_suffix = "{$language}:{$key}";
     369
     370                // Try the cache
     371                if ( false !== ( $cache = $this->cache_get( $slug, $branch, $cache_suffix ) ) ) {
     372                        // DEBUG
     373                        // var_dump( array( $slug, $branch, $cache_suffix, $cache ) );
     374                        return $cache;
     375                }
     376
     377                $originals = $this->get_gp_originals( $slug, $branch, $key, $content );
     378
     379                if ( empty( $originals ) )
     380                        return $content;
     381
     382                $translation_set_id = $this->get_gp_translation_set_id( $slug, $branch, $language );
     383
     384                if ( empty( $translation_set_id ) )
     385                        return $content;
     386
     387                foreach ( $originals as $original ) {
     388                        if ( empty( $original->id ) )
     389                                continue;
     390
     391                        $translation = $this->db->get_var( $this->db->prepare(
     392                                'SELECT translation_0 FROM translate_translations WHERE original_id = %d AND translation_set_id = %d AND status = %s',
     393                                $original->id, $translation_set_id, 'current'
     394                        ) );
     395
     396                        if ( empty( $translation ) )
     397                                continue;
     398
     399                        $content = $this->translate_gp_original( $original->singular, $translation, $content );
     400                }
     401
     402                $this->cache_set( $slug, $branch, $content, $cache_suffix );
     403
     404                return $content;
     405        }
     406
     407        function cache_key( $slug, $branch, $suffix = null ) {
     408                // EG keys
     409                // plugin:press-this:stable-readme:originals
     410                // plugin:press-this:stable-readme:original:title
     411                // plugin:press-this:stable-readme:fr:title
     412                $key = "plugin:{$slug}:{$branch}";
     413                if ( !empty( $suffix ) )
     414                        $key .= ":{$suffix}";
     415                return $key;
     416        }
     417
     418        function cache_get( $slug, $branch, $suffix = null ) {
     419                $key = $this->cache_key( $slug, $branch, $suffix );
     420                // DEBUG
     421                // wp_cache_delete( $key, $this->i18n_cache_group );
     422                return wp_cache_get( $key, $this->i18n_cache_group );
     423        }
     424
     425        function cache_set( $slug, $branch, $content, $suffix = null ) {
     426                $key = $this->cache_key( $slug, $branch, $suffix );
     427                return wp_cache_set( $key, $content, $this->i18n_cache_group );
     428        }
     429
     430        function get_gp_branch_id( $slug, $branch ) {
     431                $cache_suffix = "branch_id";
     432
     433                if ( false !== ( $branch_id = $this->cache_get( $slug, $branch, $cache_suffix ) ) )
     434                        return $branch_id;
     435
     436                $branch_id = $this->db->get_var( $this->db->prepare(
     437                        'SELECT id FROM translate_projects WHERE path = %s',
     438                        "wp-plugins/{$slug}/{$branch}"
     439                ) );
     440
     441                if ( empty( $branch_id ) )
     442                        $branch_id = 0;
     443
     444                $this->cache_set( $slug, $branch, $branch_id, $cache_suffix );
     445
     446                return $branch_id;
     447        }
     448
     449        function get_gp_originals( $slug, $branch, $key, $str ) {
     450                // Try to get a single original with the whole content first (title, etc), if passed, or get them all otherwise.
     451                if ( !empty( $key ) && !empty( $str ) ) {
     452                        $originals = $this->search_gp_original( $slug, $branch, $key, $str );
     453                        if ( !empty( $originals ) )
     454                                return array( $originals );
     455                        // Do not cache this as originals, search_gp_original() does its own caching
     456                }
     457
     458                $cache_suffix = 'originals';
     459
     460                if ( false !== ( $originals = $this->cache_get( $slug, $branch, $cache_suffix ) ) )
     461                        return $originals;
     462
     463                $branch_id = $this->get_gp_branch_id( $slug, $branch );
     464
     465                if ( empty( $branch_id ) )
     466                        return array();
     467
     468                $originals = $this->db->get_results( $this->db->prepare(
     469                        'SELECT id, singular, comment FROM translate_originals WHERE project_id = %d AND status = %s',
     470                        $branch_id, '+active'
     471                ) );
     472
     473                if ( empty( $originals ) )
     474                        $originals = array(); // still cache if empty, but as array, never false
     475
     476                $this->cache_set( $slug, $branch, $originals, $cache_suffix );
     477
     478                return $originals;
     479        }
     480
     481        function get_gp_translation_set_id( $slug, $branch, $locale ) {
     482                $locale = strtolower( $locale );
     483
     484                if ( false !== strpos( $locale, '_' ) ) {
     485                        $locale = str_replace( '_', '-', $locale );
     486                }
     487
     488                $cache_suffix = "{$locale}:translation_set_id";
     489
     490                if ( false !== ( $translation_set_id = $this->cache_get( $slug, $branch, $cache_suffix ) ) )
     491                        return $translation_set_id;
     492
     493                $branch_id = $this->get_gp_branch_id( $slug, $branch );
     494
     495                if ( empty( $branch_id ) )
     496                        return 0;
     497
     498                $translation_set_id = $this->db->get_var( $this->db->prepare(
     499                        'SELECT id FROM translate_translation_sets WHERE project_id = %d AND locale = %s',
     500                        $branch_id, $locale ) );
     501
     502                if ( empty( $translation_set_id ) ) {
     503                        // Don't give up yet. Might be given fr_FR, which actually exists as locale=fr in GP.
     504                        $translation_set_id = $this->db->get_var( $this->db->prepare(
     505                                'SELECT id FROM translate_translation_sets WHERE project_id = %d AND locale = %s',
     506                                $branch_id, substr( $locale, 0, 2 ) ) );
     507                }
     508
     509                if ( empty( $translation_set_id ) )
     510                        $translation_set_id = 0;
     511
     512                $this->cache_set( $slug, $branch, $translation_set_id, $cache_suffix );
     513
     514                return $translation_set_id;
     515        }
     516
     517        function search_gp_original( $slug, $branch, $key, $str ) {
     518                $cache_suffix = "original:{$key}";
     519
     520                if ( false !== ( $original = $this->cache_get( $slug, $branch, $cache_suffix ) ) )
     521                        return $original;
     522
     523                $branch_id = $this->get_gp_branch_id( $slug, $branch );
     524
     525                if ( empty( $branch_id ) )
     526                        return false;
     527
     528                $original = $this->db->get_row( $this->db->prepare(
     529                        'SELECT id, singular, comment FROM translate_originals WHERE project_id = %d AND status = %s AND singular = %s',
     530                        $branch_id, '+active', $str
     531                ) );
     532
     533                if ( empty( $original ) )
     534                        $original = null;
     535
     536                $this->cache_set( $slug, $branch, $original, $cache_suffix );
     537
     538                return $original;
     539        }
     540
     541        function translate_gp_original( $original, $translation, $content) {
     542                $content = str_replace( $original, $translation, $content );
     543                return $content;
     544        }
     545
     546        function verify_subdomain( $locale ) {
     547                if ( empty( $locale ) )
     548                        return '';
     549
     550                $cache_key = "subdomains:{$locale}";
     551
     552                if ( false !== ( $subdomain = wp_cache_get( $cache_key, $this->i18n_cache_group ) ) ) {
     553                        // var_dump(array($cache_key, $subdomain));
     554                        return $subdomain;
     555                }
     556
     557                $subdomain = '';
     558
     559                if ( 2 === strlen( $locale ) ) {
     560                        $subdomain = $this->db->get_var( $this->db->prepare(
     561                                'SELECT subdomain FROM locales WHERE subdomain = %s LIMIT 1',
     562                                $locale
     563                        ) );
     564                } else if ( 5 === strlen( $locale ) ) {
     565                        $subdomain = $this->db->get_var( $this->db->prepare(
     566                                'SELECT subdomain FROM locales WHERE locale = %s LIMIT 1',
     567                                $locale
     568                        ) );
     569                }
     570
     571                wp_cache_set( $cache_key, $subdomain, $this->i18n_cache_group );
     572
     573                return $subdomain;
     574        }
     575}
     576 No newline at end of file
  • extend/plugins-plugins/svn-track/class.dotorg-plugins-tracker.php

    Property changes on: extend/plugins-plugins/svn-track/class.dotorg-plugins-i18n.php
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    9393                                bb_set_current_user( $user->ID );
    9494                        }
    9595
    96                         if ( 1 < $argc && 'update' == $argv[1] ) {
     96                        if ( 1 < $argc && ( 'update' == $argv[1] || 'i18n' == $argv[1] ) ) {
    9797                                if ( !isset( $_SERVER['REMOTE_ADDR'] ) )
    9898                                        $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
    9999
    100100                                if ( 2 == $argc ) // php bb-load.php update
    101101                                        return $this->process_changes();
    102102
     103                                if ( 'i18n' == $argv[1] ) {
     104                                        // php bb-load.php i18n slug [dev|stable, default: dev] [code|readme|all, default: all]
     105                                        return $this->process_i18n( $argv[2], ( 'stable' === $argv[3] ) ? $argv[3] : 'dev', $argv[4] );
     106                                }
     107
    103108                                switch ( $argv[2] ) {
    104109                                case 'all' :
    105110                                        return $this->process_all();
     
    277282                        $content = preg_replace_callback( "#<img class='screenshot' src='(screenshot-[^']+)' alt='[^']+' />#", array( &$this, '_screenshot_img_tag' ), $content );
    278283                }
    279284
    280                 return $content;
     285                return $this->translate( $show, $content );
    281286        }
    282287
     288        function i18n_obj( $db, $tracker = null ) {
     289                if ( !class_exists( 'Dotorg_Plugin_I18n' ) )
     290                        require_once( __DIR__ . '/class.dotorg-plugins-i18n.php' );
     291                return new Dotorg_Plugin_I18n( $db, $tracker );
     292        }
     293
     294        function translate( $key, $content, $args = array() ) {
     295                return $this->i18n_obj( $this->db )->translate( $key, $content, $args );
     296        }
     297
    283298        function _screenshot_shortcode( $matches ) {
    284299                return $this->_screenshot_image( $matches[1], $matches[2] );
    285300        }
     
    11681183                                $path_rels = array_merge( $path_rels, array_keys( $entry->paths ) );
    11691184                        }
    11701185                        $return_revision = array_shift( array_keys( $triggers ) );
    1171                 } else { // its some paths
     1186                } else { // its some paths
    11721187                        $path_rels =& $triggers;
    11731188                        $return_revision = true;
    11741189                }
     
    15621577                        else
    15631578                                bb_delete_topicmeta( $topic_id, $key );
    15641579                }
     1580
     1581                // Reprocess the readme for translation.
     1582                // DEBUG: placeholder, untested and not ready as of yet.
     1583                // $this->process_i18n( $slug, 'stable', 'readme' ); // Figures on its own if stable means dev (trunk).
    15651584        }
    15661585
    15671586        function load_topic_page( $topic_id ) {
     
    17311750                        return 'http://wordpress.org/support/view/plugin-reviews/' . $topic->topic_slug;
    17321751                if ( bb_get_option( 'mod_rewrite' ) ) {
    17331752                        if ( 'description' == $section )
    1734                                 return rtrim( get_topic_link( $topic_id ), '/' ) . '/';
    1735                         return rtrim( get_topic_link( $topic_id ), '/' ) . "/$section/";
     1753                                return $this->localize_section_url( rtrim( get_topic_link( $topic_id ), '/' ) . '/' );
     1754                        return  $this->localize_section_url( rtrim( get_topic_link( $topic_id ), '/' ) . "/$section/" );
    17361755                } else {
    17371756                        if ( 'description' == $section )
    1738                                 return remove_query_arg( 'show', get_topic_link( $topic_id ) );
    1739                         return add_query_arg( 'show', $section, get_topic_link( $topic_id ) );
     1757                                return  $this->localize_section_url( remove_query_arg( 'show', get_topic_link( $topic_id ) ) );
     1758                        return  $this->localize_section_url( add_query_arg( 'show', $section, get_topic_link( $topic_id ) ) );
    17401759                }
    17411760        }
    17421761
     1762        function localize_section_url( $url ) {
     1763                $server_name = strtolower( $_SERVER[ 'SERVER_NAME' ] );
     1764                if ( 'wordpress.org' != $server_name && preg_match( '/\.wordpress\.org$/', $server_name ) )
     1765                        return str_replace( '://wordpress.org/', "://{$server_name}/", $url );
     1766                return $url;
     1767        }
     1768
    17431769        function section_title( $section ) {
    17441770                switch ( $section ) {
    17451771                        case 'description':
     
    19962022                return false;
    19972023        }
    19982024
     2025        function process_i18n( $slug, $branch = 'dev', $type = 'all' ) {
     2026                return $this->i18n_obj( $this->db, $this )->process( $slug, $branch, $type );
     2027        }
     2028
    19992029        function get_all_roots( $via = 'local' ) {
    20002030                global $bbdb;
    20012031                $root_rels = false;
  • bb-theme/plugins/search.php

     
    3232
    3333        echo wporg_get_plugin_icon( $topic );
    3434?>
    35         <h3><a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></h3>
     35        <h3><a href="<?php topic_link(); ?>"><?php echo $svn_tracker->translate( 'title', get_topic_title() ); ?></a></h3>
    3636        <?php echo $svn_tracker->_post_section( 'short_description', $bb_post->post_text, 0); ?>
    3737        <ul class="plugin-meta">
    3838                <li><?php $svn_tracker->template( 'version', 'before=<span class="info-marker">'. __( 'Version:', 'wporg' ) .'</span> '); ?></li>
  • bb-theme/plugins/topic-404.php

     
    7070
    7171<div class="plugin-block">
    7272        <?php $topic = get_topic( $bb_post->topic_id ); ?>
    73         <h3><a href="<?php topic_link(); ?>"><?php topic_title(); ?></a></h3>
     73        <h3><a href="<?php topic_link(); ?>"><?php echo $svn_tracker->translate( 'title', get_topic_title() ); ?></a></h3>
    7474        <?php post_text(); ?>
    7575        <ul class="plugin-meta">
    7676                <li><?php $svn_tracker->template( 'version', 'before=<span class="info-marker">Version</span> '); ?></li>
  • bb-theme/plugins/topic.php

     
    4444</style>
    4545<?php endif; ?>
    4646
    47         <h2 itemprop="name"><?php echo apply_filters( 'topic_title', $topic->topic_title ); ?></h2>
     47        <h2 itemprop="name"><?php echo apply_filters( 'topic_title', $svn_tracker->translate( 'title', $topic->topic_title ) ); ?></h2>
    4848</div>
    4949
    5050<div id="plugin-description">
  • translate/bin/set-wp-plugin-project.php

     
     1<?php
     2
     3// Test script arguments.
     4
     5if ( $argc < 2 || empty( $argv[1] ) ) {
     6        echo "Usage: $argv[0] plugin-slug [list of non-en languages subdomains we support, no spaces]\n";
     7        echo "Example:\n$argv[0] akismet\n";
     8        exit( 1 );
     9}
     10
     11/*
     12 * Define environments, paths, supported languages.
     13 */
     14
     15$master_project_path = 'wp-plugins';
     16$plugin_slug         = trim( $argv[1] );
     17$plugin_project_path = "{$master_project_path}/{$plugin_slug}";
     18
     19/*
     20 * Make sure we want to process that plugin, or exit on failure.
     21 * TODO: remove when we go live for all plugins
     22 */
     23
     24$valid_plugins = array(
     25        'akismet', 'wpcat2tag-importer', 'wordpress-importer',
     26        'utw-importer', 'textpattern-importer', 'stp-importer',
     27        'rss-importer', 'opml-importer', 'movabletype-importer',
     28        'livejournal-importer', 'greymatter-importer', 'dotclear-importer',
     29        'blogware-importer', 'blogger-importer', 'tumblr-importer',
     30        'bbpress', 'buddypress',
     31);
     32
     33if ( !in_array( $plugin_slug, $valid_plugins ) ) {
     34        echo "Sorry, but {$plugin_slug} is not a plugin we wish to process at the moment.\n";
     35        exit( 1 );
     36}
     37
     38/*
     39 *  Load GP.
     40 */
     41
     42include __DIR__ . '/../glotpress/gp-load.php';
     43
     44/*
     45 * Get data for master parent project, or exit on failure.
     46 */
     47
     48$master_project = GP::$project->by_path( $master_project_path );
     49
     50if ( empty( $master_project ) ) {
     51        echo "Sorry, but couldn't find https://translate.wordpress.org/projects/{$master_project_path}.\n";
     52        exit( 1 );
     53}
     54
     55/*
     56 * Get the plugin's code, and name from the latter, or exit on failure.
     57 */
     58
     59$plugin_code_content = file_get_contents( "https://plugins.svn.wordpress.org/{$plugin_slug}/trunk/{$plugin_slug}.php" );
     60
     61if ( empty( $plugin_code_content ) ) {
     62        echo "Sorry, but couldn't find https://plugins.svn.wordpress.org/{$plugin_slug}/trunk/{$plugin_slug}.php.\n";
     63        exit( 1 );
     64}
     65
     66$plugin_name = search_string( $plugin_code_content, '/Plugin Name: (.+)/' );
     67
     68/*
     69 * Get or create the plugin GP project, or exit on failure
     70 */
     71
     72$plugin_project = GP::$project->by_path( $plugin_project_path );
     73
     74if ( empty( $plugin_project ) ) {
     75        $plugin_desc = search_string( $plugin_code_content, '/Description: (.+)/' );
     76
     77        $plugin_project = GP::$project->create( new GP_Project( array(
     78                'name'              => $plugin_name,
     79                'slug'              => $plugin_slug,
     80                'parent_project_id' => $master_project->id,
     81                'description'       => $plugin_desc,
     82                'active'            => 1,
     83        ) ) );
     84
     85        if ( empty( $plugin_project ) ) {
     86                echo "Sorry, but couldn't create nonexistent https://translate.wordpress.org/projects/{$plugin_project_path}.\n";
     87                exit( 1 );
     88        }
     89
     90        echo "Created https://translate.wordpress.org/projects/{$plugin_project_path}.\n";
     91}
     92
     93/*
     94 * Define if this plugin will need both a dev and stable branch, or just the former.
     95 * Exit if we can't find a readme file or declared stable tag (supports trunk as the latter).
     96 */
     97
     98$plugin_readme_content = file_get_contents( "https://plugins.svn.wordpress.org/{$plugin_slug}/trunk/readme.txt" );
     99
     100if ( empty( $plugin_readme_content ) ) {
     101        echo "Sorry, but couldn't find https://plugins.svn.wordpress.org/{$plugin_slug}/trunk/readme.txt.\n";
     102        exit( 1 );
     103}
     104
     105$stable_tag = search_string( $plugin_readme_content, '/Stable tag: (trunk|[\d\.]+)/' );
     106
     107if ( empty( $stable_tag ) ) {
     108        echo "Sorry, but couldn't find a stable tag in https://plugins.svn.wordpress.org/{$plugin_slug}/trunk/readme.txt.\n";
     109        exit( 1 );
     110}
     111
     112$needs_stable = ( 'trunk' !== $stable_tag ) ? true : false;
     113
     114/*
     115 * Deal with the always-existing dev and dev-readme branches.
     116 */
     117
     118$plugin_dev_branches_projects = handle_plugin_project_branches( $plugin_project_path, $plugin_name, $plugin_project->id, false );
     119
     120/*
     121 * Deal with the stable and stable-readme branches, if needed.
     122 */
     123
     124if ( $needs_stable ) {
     125        $plugin_stable_branches_projects = handle_plugin_project_branches( $plugin_project_path, $plugin_name, $plugin_project->id, true );
     126}
     127
     128/*
     129 * Deal with initial translation sets.
     130 * Using https://translate.wordpress.org/projects/wp-plugins/wordpress-importer/dev as a template for defaults.
     131 */
     132
     133$wp_importer_project = GP::$project->by_path( "{$master_project_path}/wordpress-importer/dev" );
     134
     135if ( ! empty( $wp_importer_project ) && !empty( $plugin_dev_branches_projects ) ) {
     136        $translation_sets = (array) GP::$translation_set->by_project_id( $wp_importer_project->id );
     137
     138        if ( !empty( $translation_sets ) ) {
     139                add_translation_sets_to_branches( $translation_sets, $plugin_dev_branches_projects );
     140
     141                if ( ! empty( $plugin_stable_branches_projects ) ) {
     142                        add_translation_sets_to_branches( $translation_sets, $plugin_stable_branches_projects );
     143                }
     144        }
     145}
     146
     147/*
     148 * We're done!
     149 */
     150echo "All set with https://translate.wordpress.org/projects/{$plugin_project_path}\n";
     151exit( 0 );
     152
     153
     154
     155
     156function search_string( $text, $pattern ) {
     157        if ( empty( $text ) || empty( $pattern ) )
     158                return '';
     159        preg_match( $pattern, $text, $matches );
     160        if ( empty( $matches ) || empty( $matches[1] ) || is_array( $matches[1] ) )
     161                return '';
     162        // Do clean out potential comment closing delimiter (*/) out of string
     163        return preg_replace( '|^(.+)[\s]+?\*/$|', '\1', trim( $matches[1] ) );
     164}
     165
     166function handle_plugin_project_branches( $plugin_project_path, $plugin_name, $parent_id, $for_stable ) {
     167        if ( true === $for_stable ) {
     168                $branch_slug = 'stable';
     169        } else {
     170                $branch_slug = 'dev';
     171                $for_stable  = false;
     172        }
     173
     174        $code_branch_project_path = "{$plugin_project_path}/{$branch_slug}";
     175        $code_branch_project      = GP::$project->by_path( $code_branch_project_path );
     176
     177        if ( empty( $code_branch_project ) ) {
     178                $code_branch_project = GP::$project->create( new GP_Project( array(
     179                        'name'              => ( $for_stable ) ? 'Stable (latest release)' : 'Development (trunk)',
     180                        'slug'              => $branch_slug,
     181                        'parent_project_id' => $parent_id,
     182                        'description'       => ( ( $for_stable ) ? 'Stable' : 'Development' ) . " version of the {$plugin_name} plugin.",
     183                        'active'            => 1,
     184                ) ) );
     185
     186                if ( empty( $code_branch_project ) ) {
     187                        echo "Sorry, but couldn't create nonexistent https://translate.wordpress.org/projects/{$code_branch_project_path}.\n";
     188                } else {
     189                        echo "Created https://translate.wordpress.org/projects/{$code_branch_project_path}.\n";
     190                }
     191        }
     192
     193        $readme_branch_project_path = "{$plugin_project_path}/{$branch_slug}-readme";
     194        $readme_branch_project      = GP::$project->by_path( $readme_branch_project_path );
     195
     196        if ( empty( $readme_branch_project ) ) {
     197                $readme_branch_project = GP::$project->create( new GP_Project( array(
     198                        'name'              => ( $for_stable ) ? 'Stable Readme (latest release)' : 'Development Readme (trunk)',
     199                        'slug'              => "{$branch_slug}-readme",
     200                        'parent_project_id' => $parent_id,
     201                        'description'       => ( ( $for_stable ) ? 'Stable' : 'Development' ) . " version of the {$plugin_name} plugin's readme.txt file.",
     202                        'active'            => 1,
     203                ) ) );
     204
     205                if ( empty( $readme_branch_project ) ) {
     206                        echo "Sorry, but couldn't create nonexistent https://translate.wordpress.org/projects/{$readme_branch_project_path}.\n";
     207                } else {
     208                        echo "Created https://translate.wordpress.org/projects/{$readme_branch_project_path}.\n";
     209                }
     210        }
     211
     212        return array( 'code' => $code_branch_project, 'readme' => $readme_branch_project );
     213}
     214
     215function add_translation_sets_to_branches( $translation_sets, $branches_projects ) {
     216        foreach( (array) $branches_projects as $project ) {
     217                if ( empty( $project ) || empty( $project->id ) )
     218                        continue;
     219
     220                foreach ( $translation_sets as $ts ) {
     221                        if ( empty( $ts ) || empty( $ts->name ) )
     222                                continue;
     223
     224                        $existing = GP::$translation_set->by_project_id_slug_and_locale( $project->id, $ts->slug, $ts->locale );
     225
     226                        if ( !empty($existing) )
     227                                continue;
     228
     229                        $new_ts = GP::$translation_set->create( array(
     230                                'project_id' => $project->id,
     231                                'name'       => $ts->name,
     232                                'locale'     => $ts->locale,
     233                                'slug'       => $ts->slug,
     234                        ) );
     235
     236                        if ( empty( $new_ts ) ) {
     237                                echo "Sorry, but couldn't create nonexistent https://translate.wordpress.org/projects/{$project->path}/{$ts->locale}/{$ts->slug}.\n";
     238                        } else {
     239                                echo "Created https://translate.wordpress.org/projects/{$project->path}/{$ts->locale}/{$ts->slug}.\n";
     240                        }
     241                }
     242        }
     243}