WordPress.org

Making WordPress.org

Ticket #760: 760.2.diff

File 760.2.diff, 15.8 KB (added by stephdau, 6 years ago)

Implements feedback so far, and introduces some experimental display related methods

  • 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">
  • extend/plugins-plugins/svn-track/class.dotorg-plugins-tracker.php

     
    3737
    3838        var $popular_cache_key = 'popular_plugins11';
    3939
     40        // TODO: remove when we launch for all plugins.
     41        var $translated_plugins = array(
     42                'blogware-importer',
     43                'livejournal-importer',
     44        );
     45
    4046        function __construct( $db, $svn_url, $svn_admin_class = false ) {
    4147                $this->db = $db;
    4248                if ( $svn_admin_class ) {
     
    9399                                bb_set_current_user( $user->ID );
    94100                        }
    95101
    96                         if ( 1 < $argc && 'update' == $argv[1] ) {
     102                        if ( 1 < $argc && ( 'update' == $argv[1] || 'i18n' == $argv[1] ) ) {
    97103                                if ( !isset( $_SERVER['REMOTE_ADDR'] ) )
    98104                                        $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
    99105
    100106                                if ( 2 == $argc ) // php bb-load.php update
    101107                                        return $this->process_changes();
    102108
     109                                if ( 'i18n' == $argv[1] ) {
     110                                        // php bb-load.php i18n slug [dev|stable, default: dev] [code|readme|all, default: all]
     111                                        return $this->process_i18n( $argv[2], ( 'stable' === $argv[3] ) ? $argv[3] : 'dev', $argv[4] );
     112                                }
     113
    103114                                switch ( $argv[2] ) {
    104115                                case 'all' :
    105116                                        return $this->process_all();
     
    277288                        $content = preg_replace_callback( "#<img class='screenshot' src='(screenshot-[^']+)' alt='[^']+' />#", array( &$this, '_screenshot_img_tag' ), $content );
    278289                }
    279290
     291                return $this->translate( $show, $content );
     292        }
     293
     294        function translate( $key, $content ) {
     295                global $topic;
     296
     297                if ( empty( $key ) || empty( $content ) )
     298                        return $content;
     299
     300                /*
     301                 * DEBUG: Getting $language from host name, should use $locale and/or $l10n globals
     302                 * once set properly when on localized site and get it from that.
     303                 * $locale is currently en_US on localized sites (eg: fr.wordpress.org)
     304                 * $i18n is currently false on localized sites (eg: fr.wordpress.org)
     305                 */
     306                $language = str_replace( '.wordpress.org', '', $_SERVER[ 'SERVER_NAME' ] );
     307
     308                if ( empty( $language ) || 2 !== strlen( $language ) )
     309                        return $content;
     310
     311                $slug = $topic->plugin_san;
     312
     313                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     314                // TODO: remove when we launch for all plugins.
     315                if ( empty( $slug ) || ! in_array( $slug, $this->translated_plugins ) )
     316                        return $content;
     317
     318                $branch    = ( empty( $topic->stable_tag ) || 'trunk' === $topic->stable_tag ) ? 'dev' : 'stable';
     319
     320                // Try to get a single original with the whole content first (title, etc), or get them all otherwise.
     321                $originals = $this->_search_gp_original( $slug, $branch, $content );
     322                if ( empty( $originals ) )
     323                        $originals = $this->_get_gp_originals( $slug, $branch );
     324
     325                if ( empty( $originals ) )
     326                        return $content;
     327
     328                $translation_set_id = $this->_get_gp_translation_set_id( $slug, $branch, $language );
     329
     330                if ( empty( $translation_set_id ) )
     331                        return $content;
     332
     333                foreach ( $originals as $original ) {
     334                        if ( empty( $original->id ) )
     335                                continue;
     336
     337                        $translation = $this->db->get_var( $this->db->prepare(
     338                                'SELECT translation_0 FROM translate_translations WHERE original_id = %d AND translation_set_id = %d AND status = %s',
     339                                $original->id, $translation_set_id, 'current'
     340                        ) );
     341
     342                        if ( empty( $translation ) )
     343                                continue;
     344
     345                        $content = $this->_translate_gp_original( $original->singular, $translation, $content );
     346                }
     347
    280348                return $content;
    281349        }
    282350
     351        function _get_gp_branch_id( $slug, $branch ) {
     352                $branch_id = $this->db->get_var( $this->db->prepare(
     353                        'SELECT id FROM translate_projects WHERE path = %s',
     354                        "wp-plugins/{$slug}/{$branch}-readme"
     355                ) );
     356
     357                return ( empty( $branch_id ) ) ? 0 : $branch_id;
     358        }
     359
     360        function _get_gp_originals( $slug, $branch ) {
     361                $branch_id = $this->_get_gp_branch_id( $slug, $branch );
     362
     363                if ( empty( $branch_id ) )
     364                        return array();
     365
     366                $originals = $this->db->get_results( $this->db->prepare(
     367                        'SELECT id, singular, comment FROM translate_originals WHERE project_id = %d AND status = %s',
     368                        $branch_id, '+active'
     369                ) );
     370
     371                if ( empty( $originals ) )
     372                        return array();
     373
     374                return $originals;
     375        }
     376
     377        function _get_gp_translation_set_id( $slug, $branch, $locale ) {
     378                $branch_id = $this->_get_gp_branch_id( $slug, $branch );
     379
     380                if ( empty( $branch_id ) )
     381                        return 0;
     382
     383                $translation_set_id = $this->db->get_var( $this->db->prepare(
     384                        'SELECT id FROM translate_translation_sets WHERE project_id = %d AND locale = %s',
     385                        $branch_id, $locale ) );
     386
     387                return ( empty( $translation_set_id ) ) ? 0 : $translation_set_id;
     388        }
     389
     390        function _search_gp_original( $slug, $branch, $str ) {
     391                $branch_id = $this->_get_gp_branch_id( $slug, $branch );
     392
     393                if ( empty( $branch_id ) )
     394                        return array();
     395
     396                $original = $this->db->get_row( $this->db->prepare(
     397                        'SELECT id, singular, comment FROM translate_originals WHERE project_id = %d AND status = %s AND singular = %s',
     398                        $branch_id, '+active', $str
     399                ) );
     400
     401                if ( empty( $original ) )
     402                        return array();
     403
     404                return array( $original );
     405        }
     406
     407        function _translate_gp_original( $original, $translation, $content) {
     408                $content = str_replace( $original, $translation, $content );
     409                return $content;
     410        }
     411
    283412        function _screenshot_shortcode( $matches ) {
    284413                return $this->_screenshot_image( $matches[1], $matches[2] );
    285414        }
     
    17311860                        return 'http://wordpress.org/support/view/plugin-reviews/' . $topic->topic_slug;
    17321861                if ( bb_get_option( 'mod_rewrite' ) ) {
    17331862                        if ( 'description' == $section )
    1734                                 return rtrim( get_topic_link( $topic_id ), '/' ) . '/';
    1735                         return rtrim( get_topic_link( $topic_id ), '/' ) . "/$section/";
     1863                                return $this->localize_section_url( rtrim( get_topic_link( $topic_id ), '/' ) . '/' );
     1864                        return  $this->localize_section_url( rtrim( get_topic_link( $topic_id ), '/' ) . "/$section/" );
    17361865                } else {
    17371866                        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 ) );
     1867                                return  $this->localize_section_url( remove_query_arg( 'show', get_topic_link( $topic_id ) ) );
     1868                        return  $this->localize_section_url( add_query_arg( 'show', $section, get_topic_link( $topic_id ) ) );
    17401869                }
    17411870        }
    17421871
     1872        function localize_section_url( $url ) {
     1873                $curr_host = $_SERVER[ 'SERVER_NAME' ];
     1874                if ( 'wordpress.org' != $curr_host && preg_match( '/\.wordpress\.org$/', $curr_host ) )
     1875                        return str_replace( '://wordpress.org/', "://{$curr_host}/", $url );
     1876                return $url;
     1877        }
     1878
    17431879        function section_title( $section ) {
    17441880                switch ( $section ) {
    17451881                        case 'description':
     
    19962132                return false;
    19972133        }
    19982134
     2135        function process_i18n( $slug, $branch = 'dev', $type = 'all' ) {
     2136                if ( empty( $slug ) )
     2137                        return false;
     2138
     2139                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     2140                // TODO: remove when we launch for all plugins.
     2141                if ( ! in_array( $slug, $this->translated_plugins ) )
     2142                        return false;
     2143
     2144                if ( 'stable' !== $branch )
     2145                        $branch = 'dev';
     2146
     2147                if ( 'code' !== $type && 'readme' !== $type )
     2148                        $type = 'all';
     2149
     2150                $path_rel = "{$slug}/trunk/";
     2151
     2152                if ( 'stable' === $branch ) {
     2153                        if ( false == ( $stable_tag = $this->get_stable_tag_dir_using( $path_rel ) ) ) {
     2154                                // Can't get a stable tag, bail out
     2155                                return false;
     2156                        } else if ( 'trunk' == trim( $stable_tag['tag_dir'], '/' ) ) {
     2157                                // If stable is trunk, then it's really same as dev, switch to that
     2158                                $branch = 'dev';
     2159                        } else {
     2160                                // We're dealing with an actual stable tag, go for it
     2161                                $path_rel = "{$slug}/{$stable_tag['tag_dir']}"; //
     2162                        }
     2163                }
     2164
     2165                if ( 'code' === $type || 'all' === $type )
     2166                        $this->process_code_i18n( $path_rel, $branch );
     2167
     2168                if ( 'readme' === $type || 'all' === $type )
     2169                        $this->process_readme_i18n( $path_rel, $branch );
     2170
     2171                echo "Processed {$type} for {$path_rel}\n";
     2172                return true;
     2173        }
     2174
     2175        function process_code_i18n( $path_rel, $branch = 'dev' ) {
     2176                $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel );
     2177
     2178                if ( empty( $slug ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) )
     2179                        return false;
     2180
     2181                // DEBUG: in_array check is because we'll start the program with a finite list of plugins
     2182                // TODO: remove when we launch for all plugins.
     2183                if ( !in_array( $slug, $this->translated_plugins ) )
     2184                        return false;
     2185
     2186                $export_path = $this->create_export( $path_rel );
     2187
     2188                if ( empty( $export_path ) || !is_dir( $export_path ) )
     2189                        return false;
     2190
     2191                $old_cwd = getcwd();
     2192                chdir( $export_path );
     2193
     2194                // Check for a plugin text domain declaration and loading, grep recursively, not necessarily in [slug].php
     2195                if ( ! shell_exec( 'grep -r --include "*.php" "Text Domain: ' . escapeshellarg( $slug ) . '" .' ) && ! shell_exec( 'grep -r --include "*.php" "\bload_plugin_textdomain\b" .' ) )
     2196                        return false;
     2197
     2198                if ( !class_exists( 'PotExtMeta' ) )
     2199                        require_once( __DIR__ . '/i18n-tools/pot-ext-meta.php' );
     2200
     2201                // Create pot file from code
     2202                $pot_file = "./tmp-{$slug}.pot"; // Using tmp- prefix in case a plugin has $slug.pot committed
     2203                $makepot  = new MakePOT;
     2204
     2205                if ( ! $makepot->wp_plugin( '.', $pot_file, $slug ) || ! file_exists( $pot_file ) )
     2206                        return false;
     2207
     2208                // DEBUG
     2209                // system( "cat {$pot_file}" );
     2210
     2211                $this->import_to_glotpress_project( $slug, $branch, $pot_file );
     2212
     2213                chdir( $old_cwd );
     2214                return true;
     2215        }
     2216
     2217        function process_readme_i18n( $path_rel, $branch = 'dev' ) {
     2218                $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel );
     2219
     2220                if ( empty( $slug ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) )
     2221                        return false;
     2222
     2223                // DEBUG: in_array as separate check because we'll start the program with a finite list of plugins
     2224                // TODO: remove when we launch for all plugins.
     2225                if ( !in_array( $slug, $this->translated_plugins ) )
     2226                        return false;
     2227
     2228                $export_path = $this->create_export( $path_rel );
     2229
     2230                if ( empty( $export_path ) || !is_dir( $export_path ) )
     2231                        return false;
     2232
     2233                $old_cwd = getcwd();
     2234                chdir( $export_path );
     2235
     2236                $readme = $this->parse_readme_in( $path_rel );
     2237
     2238                if ( !class_exists( 'PO' ) )
     2239                        require_once( __DIR__ . '/i18n-tools/pomo/po.php' );
     2240
     2241                $pot = new PO;
     2242
     2243                foreach ( array( 'name', 'license', 'short_description' ) as $key ) {
     2244                        $readme[ $key ] = trim( $readme[ $key ] ) ;
     2245                }
     2246
     2247                // If empty or "sketchy", get the plugin name form the PHP file's headers
     2248                if ( empty( $readme['name'] ) || 'Plugin Name' == trim( $readme['name'] ) ) {
     2249                        // -o in grep will make sure we don't get comments opening delimiters (//, /*) or spaces as part of string
     2250                        $name_from_php  = trim( shell_exec( 'grep -o "\bPlugin Name:.*" ' . escapeshellarg( $slug ) . '.php' ) );
     2251                        // Remove the header label
     2252                        $name_from_php  = str_replace( 'Plugin Name:', '', $name_from_php );
     2253                        // Do clean out potential comment closing delimiter (*/) out of string
     2254                        $name_from_php  = preg_replace( '|^(.+)[\s]+?\*/$|', '\1', $name_from_php );
     2255                        // Use trimmed results as plugin name
     2256                        $readme['name'] = trim( $name_from_php );
     2257                }
     2258
     2259                if ( !empty( $readme['name'] ) ) {
     2260                        $pot->add_entry( new Translation_Entry ( array(
     2261                                'singular'           => $readme['name'],
     2262                                'extracted_comments' => 'Plugin/theme name.',
     2263                        ) ) );
     2264                }
     2265
     2266                if ( !empty( $readme['license'] ) ) {
     2267                        $pot->add_entry( new Translation_Entry ( array(
     2268                                'singular'           => $readme['license'],
     2269                                'extracted_comments' => 'License.',
     2270                        ) ) );
     2271                }
     2272
     2273                if ( !empty( $readme['short_description'] ) ) {
     2274                        $pot->add_entry( new Translation_Entry ( array(
     2275                                'singular'           => $readme['short_description'],
     2276                                'extracted_comments' => 'Short description.',
     2277                        ) ) );
     2278                }
     2279
     2280                if ( !empty( $readme['screenshots'] ) ) {
     2281                        foreach ( $readme['screenshots'] as $sshot_key => $sshot_desc ) {
     2282                                $sshot_desc = trim( $sshot_desc );
     2283                                $pot->add_entry( new Translation_Entry ( array(
     2284                                        'singular'           => $sshot_desc,
     2285                                        'extracted_comments' => 'Screenshot description.',
     2286                                ) ) );
     2287                        }
     2288
     2289                }
     2290
     2291                if ( empty( $readme['sections'] ) )
     2292                        $readme['sections'] = array();
     2293
     2294                // Adding remaining content as a section so it's processed by the same loop below
     2295                if ( !empty( $readme['remaining_content'] ) )
     2296                        $readme['sections']['remaining_content'] = $readme['remaining_content'];
     2297
     2298                $strings = array();
     2299
     2300                foreach ( $readme['sections'] as $section_key => $section_text ) {
     2301                        if ( 'screenshots' == $section_key )
     2302                                continue;
     2303
     2304                        /*
     2305                         * Scanned tags based on block elements found in Automattic_Readme::filter_text() $allowed.
     2306                         * Scanning H3/4, li, p and blockquote. Other tags are ignored  in strings (a, strong, cite, etc).
     2307                         * Processing notes:
     2308                         * * Don't normalize/modify original text, will be used as a search pattern in original doc in some use-cases.
     2309                         * * Using regexes over XML parsing for performance reasons, could move to the latter for more accuracy.
     2310                         */
     2311
     2312                        if ( 'changelog' !== $section_key ) { // No need to scan non-translatable version headers in changelog
     2313                                if ( preg_match_all( '|<h[3-4]+[^>]*>(.+)</h[3-4]+>|', $section_text, $matches ) ) {
     2314                                        if ( !empty( $matches[1] ) ) {
     2315                                                foreach ( $matches[1] as $text ) {
     2316                                                        $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} header" );
     2317                                                }
     2318                                        }
     2319                                }
     2320                        }
     2321
     2322                        if ( preg_match_all( '|<li>(.+)</li>|', $section_text, $matches ) ) {
     2323                                if ( !empty( $matches[1] ) ) {
     2324                                        foreach ( $matches[1] as $text ) {
     2325                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} list item" );
     2326                                        }
     2327                                }
     2328                        }
     2329
     2330                        if ( preg_match_all( '|<p>(.+)</p>|', $section_text, $matches ) ) {
     2331                                if ( !empty( $matches[1] ) ) {
     2332                                        foreach ( $matches[1] as $text ) {
     2333                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} paragraph" );
     2334                                        }
     2335                                }
     2336                        }
     2337
     2338                        if ( preg_match_all( '|<blockquote>(.+)</blockquote>|', $section_text, $matches ) ) {
     2339                                if ( !empty( $matches[1] ) ) {
     2340                                        foreach ( $matches[1] as $text ) {
     2341                                                $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} block quote" );
     2342                                        }
     2343                                }
     2344                        }
     2345                }
     2346
     2347                foreach ( $strings as $text => $comments ) {
     2348                        $pot->add_entry( new Translation_Entry ( array(
     2349                                'singular'           => $text,
     2350                                'extracted_comments' => 'Found in ' . implode( $comments, ", " ) . '.',
     2351                        ) ) );
     2352                }
     2353
     2354                $pot_file = "./tmp-{$slug}-readme.pot";
     2355                $pot->export_to_file( $pot_file );
     2356
     2357                // DEBUG
     2358                // system( "cat {$pot_file}" );
     2359
     2360                // import to GlotPress, dev or stable
     2361                $this->import_to_glotpress_project( $slug, "{$branch}-readme", $pot_file );
     2362
     2363                chdir( $old_cwd );
     2364                return true;
     2365        }
     2366
     2367        function handle_translator_comment( $array, $key, $val ) {
     2368                $val = trim( preg_replace( '/[^a-z0-9]/i', ' ', $val ) ); // cleanup key names for display
     2369                if ( empty( $array[ $key ] ) ) {
     2370                        $array[ $key ] = array( $val );
     2371                } else if ( !in_array( $val, $array[ $key ] ) ) {
     2372                        $array[ $key ][] = $val;
     2373                }
     2374                return $array;
     2375        }
     2376
     2377        function import_to_glotpress_project( $project, $branch, $file ) {
     2378                $cmd = 'php ' . __DIR__ . '/../../../translate/glotpress/scripts/import-originals.php -o po -p ' . escapeshellarg( "wp-plugins/{$project }/{$branch}" ) . ' -f ' . escapeshellarg( $file );
     2379                // DEBUG
     2380                // var_dump( $cmd );
     2381                system( $cmd );
     2382                // Note: this will only work if the GlotPress project/sub-projects exist. To be improved, or must insure they do.
     2383        }
     2384
    19992385        function get_all_roots( $via = 'local' ) {
    20002386                global $bbdb;
    20012387                $root_rels = false;