Making WordPress.org

Ticket #760: 760-wporg.diff

File 760-wporg.diff, 21.5 KB (added by stephdau, 10 years ago)

Follow up on 760.2.diff: refactors code into own file/class for cleanliness, adds full caching (see 760-translate.diff for cache handling), adds translation support in search results and listings, improves all code.

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