r97260 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r97259‎ | r97260 | r97261 >
Date:13:13, 16 September 2011
Author:nikerabbit
Status:deferred (Comments)
Tags:
Comment:
Introduced Special:MessageGroupStats
Required refactoring lots of code, including the new StatsTable class
Modified paths:
  • /trunk/extensions/Translate/Translate.alias.php (modified) (history)
  • /trunk/extensions/Translate/Translate.i18n.php (modified) (history)
  • /trunk/extensions/Translate/Translate.php (modified) (history)
  • /trunk/extensions/Translate/_autoload.php (modified) (history)
  • /trunk/extensions/Translate/specials/SpecialLanguageStats.php (modified) (history)
  • /trunk/extensions/Translate/utils/MessageGroupStats.php (modified) (history)
  • /trunk/extensions/Translate/utils/StatsTable.php (added) (history)

Diff [purge]

Index: trunk/extensions/Translate/Translate.php
@@ -67,6 +67,8 @@
6868 $wgSpecialPageGroups['TranslationStats'] = 'wiki';
6969 $wgSpecialPages['LanguageStats'] = 'SpecialLanguageStats';
7070 $wgSpecialPageGroups['LanguageStats'] = 'wiki';
 71+$wgSpecialPages['MessageGroupStats'] = 'SpecialMessageGroupStats';
 72+$wgSpecialPageGroups['MessageGroupStats'] = 'wiki';
7173 $wgSpecialPages['ImportTranslations'] = 'SpecialImportTranslations';
7274 $wgSpecialPageGroups['ImportTranslations'] = 'wiki';
7375 $wgSpecialPages['ManageMessageGroups'] = 'SpecialManageGroups';
Index: trunk/extensions/Translate/_autoload.php
@@ -65,6 +65,7 @@
6666 $wgAutoloadClasses['SpecialTranslationStats'] = $dir . 'specials/SpecialTranslationStats.php';
6767 $wgAutoloadClasses['SpecialTranslations'] = $dir . 'specials/SpecialTranslations.php';
6868 $wgAutoloadClasses['SpecialLanguageStats'] = $dir . 'specials/SpecialLanguageStats.php';
 69+$wgAutoloadClasses['SpecialMessageGroupStats'] = $dir . 'specials/SpecialMessageGroupStats.php';
6970 $wgAutoloadClasses['SpecialImportTranslations'] = $dir . 'specials/SpecialImportTranslations.php';
7071 $wgAutoloadClasses['SpecialFirstSteps'] = $dir . 'specials/SpecialFirstSteps.php';
7172 $wgAutoloadClasses['SpecialSupportedLanguages'] = $dir . 'specials/SpecialSupportedLanguages.php';
@@ -99,6 +100,7 @@
100101
101102 $wgAutoloadClasses['MessageIndexRebuilder'] = $dir . 'utils/MessageIndexRebuilder.php';
102103 $wgAutoloadClasses['MessageTable'] = $dir . 'utils/MessageTable.php';
 104+$wgAutoloadClasses['StatsTable'] = $dir . 'utils/StatsTable.php';
103105 $wgAutoloadClasses['JsSelectToInput'] = $dir . 'utils/JsSelectToInput.php';
104106 $wgAutoloadClasses['HTMLJsSelectToInputField'] = $dir . 'utils/HTMLJsSelectToInputField.php';
105107 $wgAutoloadClasses['MessageGroupCache'] = $dir . 'utils/MessageGroupCache.php';
Index: trunk/extensions/Translate/utils/StatsTable.php
@@ -0,0 +1,290 @@
 2+<?php
 3+/**
 4+ * Contains logic for special page Special:LanguageStats.
 5+ *
 6+ * @file
 7+ * @author Siebrand Mazeland
 8+ * @author Niklas Laxström
 9+ * @copyright Copyright © 2008-2011 Siebrand Mazeland, Niklas Laxström
 10+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 11+ */
 12+
 13+/**
 14+ * Implements includable special page Special:LanguageStats which provides
 15+ * translation statistics for all defined message groups.
 16+ *
 17+ * Loosely based on the statistics code in phase3/maintenance/language
 18+ *
 19+ * Use {{Special:LanguageStats/nl/1}} to show for 'nl' and suppres completely
 20+ * translated groups.
 21+ *
 22+ * @ingroup SpecialPage TranslateSpecialPage Stats
 23+ */
 24+class StatsTable {
 25+ /// @var Language
 26+ protected $lang;
 27+ /// @var SpecialPage
 28+ protected $translate;
 29+ /// @var string
 30+ protected $mainColumnHeader;
 31+
 32+ public function __construct() {
 33+ global $wgLang;
 34+ $this->lang = $wgLang;
 35+ $this->translate = SpecialPage::getTitleFor( 'Translate' );
 36+ }
 37+
 38+ /**
 39+ * Statistics table element (heading or regular cell)
 40+ *
 41+ * @param $in \string Element contents.
 42+ * @param $bgcolor \string Backround color in ABABAB format.
 43+ * @param $sort \string Value used for sorting.
 44+ * @return \string Html td element.
 45+ */
 46+ public function element( $in, $bgcolor = '', $sort = '' ) {
 47+ $attributes = array();
 48+ if ( $sort ) $attributes['data-sort-value'] = $sort;
 49+ if ( $bgcolor ) $attributes['style'] = "background-color: #" . $bgcolor;
 50+
 51+ $element = Html::element( 'td', $attributes, $in );
 52+ return $element;
 53+ }
 54+
 55+ public function getBackgroundColour( $subset, $total, $fuzzy = false ) {
 56+ $v = @round( 255 * $subset / $total );
 57+
 58+ if ( $fuzzy ) {
 59+ // Weigh fuzzy with factor 20.
 60+ $v = $v * 20;
 61+ if ( $v > 255 ) $v = 255;
 62+ $v = 255 - $v;
 63+ }
 64+
 65+ if ( $v < 128 ) {
 66+ // Red to Yellow
 67+ $red = 'FF';
 68+ $green = sprintf( '%02X', 2 * $v );
 69+ } else {
 70+ // Yellow to Green
 71+ $red = sprintf( '%02X', 2 * ( 255 - $v ) );
 72+ $green = 'FF';
 73+ }
 74+ $blue = '00';
 75+
 76+ return $red . $green . $blue;
 77+ }
 78+
 79+ public function getMainColumnHeader() {
 80+ return $this->mainColumnHeader;
 81+ }
 82+
 83+ public function setMainColumnHeader( Message $msg ) {
 84+ $this->mainColumnHeader = $this->createColumnHeader( $msg );
 85+ }
 86+
 87+ public function createColumnHeader( Message $msg ) {
 88+ return Html::element( 'th', array(), $msg->text() );
 89+ }
 90+
 91+ public function createHeader() {
 92+ // Create table header
 93+ $out = Html::openElement(
 94+ 'table',
 95+ array( 'class' => "sortable wikitable mw-sp-translate-table" )
 96+ );
 97+
 98+ $out .= "\n\t" . Html::openElement( 'thead' );
 99+ $out .= "\n\t" . Html::openElement( 'tr' );
 100+
 101+ $out .= "\n\t\t" . $this->getMainColumnHeader();
 102+ $out .= "\n\t\t" . $this->createColumnHeader(
 103+ wfMessage( 'translate-total' ) );
 104+ $out .= "\n\t\t" . $this->createColumnHeader(
 105+ wfMessage( 'translate-untranslated' ) );
 106+ $out .= "\n\t\t" . $this->createColumnHeader(
 107+ wfMessage( 'translate-percentage-complete' ) );
 108+ $out .= "\n\t\t" . $this->createColumnHeader(
 109+ wfMessage( 'translate-percentage-fuzzy' ) );
 110+ $out .= "\n\t" . Html::closeElement( 'tr' );
 111+ $out .= "\n\t" . Html::closeElement( 'thead' );
 112+ $out .= "\n\t" . Html::openElement( 'tbody' );
 113+
 114+ return $out;
 115+ }
 116+
 117+ /**
 118+ * Makes a row with aggregate numbers.
 119+ * @param $message Message
 120+ * @param $numbers array ( total, translate, fuzzy )
 121+ * @return string Html
 122+ */
 123+ public function makeTotalRow( Message $message, $numbers ) {
 124+ list( $total, $translated, $fuzzy ) = $numbers;
 125+ $out = "\t" . Html::openElement( 'tr' );
 126+ $out .= "\n\t\t" . Html::element( 'td', array(), $message->text() );
 127+ $out .= $this->makeNumberColumns( $fuzzy, $translated, $total );
 128+ return $out;
 129+ }
 130+
 131+ /**
 132+ * Makes partial row from completion numbers
 133+ * @param $fuzzy int Number of fuzzy translations
 134+ * @param $translated in Number of non-fuzzy translations
 135+ * @param $total Total number of messages in this group
 136+ * @return string Html
 137+ */
 138+ public function makeNumberColumns( $fuzzy, $translated, $total ) {
 139+ if ( $total === null ) {
 140+ $na = "\n\t\t" . Html::element( 'td', array( 'data-sort-value' => -1 ), '...' );
 141+ $nap = "\n\t\t" . $this->element( '...', 'AFAFAF', -1 );
 142+ $out = $na . $na . $nap . $nap;
 143+ $out .= "\n\t" . Xml::closeElement( 'tr' ) . "\n";
 144+ return $out;
 145+ }
 146+
 147+ $out = "\n\t\t" . Html::element( 'td',
 148+ array( 'data-sort-value' => $total ),
 149+ $this->lang->formatNum( $total ) );
 150+
 151+ $out .= "\n\t\t" . Html::element( 'td',
 152+ array( 'data-sort-value' => $total - $translated ),
 153+ $this->lang->formatNum( $total - $translated ) );
 154+
 155+ $out .= "\n\t\t" . $this->element( $this->formatPercentage( $translated / $total ),
 156+ $this->getBackgroundColour( $translated, $total ),
 157+ sprintf( '%1.3f', $translated / $total ) );
 158+
 159+ $out .= "\n\t\t" . $this->element( $this->formatPercentage( $fuzzy / $total ),
 160+ $this->getBackgroundColour( $fuzzy, $total, true ),
 161+ sprintf( '%1.3f', $fuzzy / $total ) );
 162+
 163+ $out .= "\n\t" . Xml::closeElement( 'tr' ) . "\n";
 164+ return $out;
 165+ }
 166+
 167+ /**
 168+ * Makes a nice print from plain float.
 169+ * @param $num float
 170+ * @return string Plain text
 171+ */
 172+ public function formatPercentage( $num ) {
 173+ $fmt = $this->lang->formatNum( number_format( round( 100 * $num, 2 ), 2 ) );
 174+ return wfMessage( 'percent', $fmt )->text();
 175+ }
 176+
 177+ /**
 178+ * Gets the name of group with some extra formatting.
 179+ * @param $group MessageGroup
 180+ * @return string Html
 181+ */
 182+ public function getGroupLabel( MessageGroup $group ) {
 183+ $groupLabel = htmlspecialchars( $group->getLabel() );
 184+
 185+ // Bold for meta groups.
 186+ if ( $group->isMeta() ) {
 187+ $groupLabel = Html::rawElement( 'b', null, $groupLabel );
 188+ }
 189+
 190+ return $groupLabel;
 191+ }
 192+
 193+ /**
 194+ * Gets the name of group linked to translation tool.
 195+ * @param $group MessageGroup
 196+ * @param $code string Language code
 197+ * @param $paras array Any extra query parameters.
 198+ * @return string Html
 199+ */
 200+ public function makeGroupLink( MessageGroup $group, $code, $params ) {
 201+
 202+ $queryParameters = $params + array(
 203+ 'group' => $group->getId(),
 204+ 'language' => $code
 205+ );
 206+
 207+ $attributes = array(
 208+ 'title' => $this->getGroupDescription( $group )
 209+ );
 210+
 211+ $linker = class_exists( 'DummyLinker' ) ? new DummyLinker : new Linker;
 212+ $translateGroupLink = $linker->link(
 213+ $this->translate, $this->getGroupLabel( $group ), $attributes, $queryParameters
 214+ );
 215+
 216+ return $translateGroupLink;
 217+ }
 218+
 219+ /**
 220+ * Gets the description of a group. This is a bit slow thing to do for
 221+ * thousand+ groups, so some caching is involved.
 222+ * @param $group MessageGroup
 223+ * @return string Plain text
 224+ */
 225+ public function getGroupDescription( MessageGroup $group ) {
 226+ $code = $this->lang->getCode();
 227+
 228+ $cache = wfGetCache( CACHE_ANYTHING );
 229+ $key = wfMemckey( "translate-groupdesc-$code-" . $group->getId() );
 230+ $desc = $cache->get( $key );
 231+ if ( is_string( $desc ) ) {
 232+ return $desc;
 233+ }
 234+
 235+ $realFunction = array( 'MessageCache', 'singleton' );
 236+ if ( is_callable( $realFunction ) ) {
 237+ $mc = MessageCache::singleton();
 238+ } else {
 239+ global $wgMessageCache;
 240+ $mc = $wgMessageCache;
 241+ }
 242+ $desc = $mc->transform( $group->getDescription(), true, $this->lang, $this->getTitle() );
 243+ $cache->set( $key, $desc );
 244+ return $desc;
 245+ }
 246+
 247+ /**
 248+ * Check whether translations in given group in given language
 249+ * has been disabled.
 250+ * @param $groupId string \Message group id
 251+ * @param $code string \Language code
 252+ * @return bool
 253+ */
 254+ public function isBlacklisted( $groupId, $code ) {
 255+ global $wgTranslateBlacklist;
 256+
 257+ $blacklisted = null;
 258+
 259+ $checks = array(
 260+ $groupId,
 261+ strtok( $groupId, '-' ),
 262+ '*'
 263+ );
 264+
 265+ foreach ( $checks as $check ) {
 266+ $blacklisted = @$wgTranslateBlacklist[$check][$code];
 267+
 268+ if ( $blacklisted !== null ) {
 269+ break;
 270+ }
 271+ }
 272+
 273+ return $blacklisted;
 274+ }
 275+
 276+ /**
 277+ * Used to circumvent ugly tooltips when newlines are used in the
 278+ * message content ("x\ny" becomes "x y").
 279+ */
 280+ public static function formatTooltip( $text ) {
 281+ $wordSeparator = wfMessage( 'word-separator' )->text();
 282+
 283+ $text = strtr( $text, array(
 284+ "\n" => $wordSeparator,
 285+ "\r" => $wordSeparator,
 286+ "\t" => $wordSeparator,
 287+ ) );
 288+
 289+ return $text;
 290+ }
 291+}
Property changes on: trunk/extensions/Translate/utils/StatsTable.php
___________________________________________________________________
Added: svn:eol-style
1292 + native
Index: trunk/extensions/Translate/utils/MessageGroupStats.php
@@ -158,9 +158,9 @@
159159 */
160160 protected static function extractNumbers( $row ) {
161161 return array(
162 - $row->tgs_total,
163 - $row->tgs_translated,
164 - $row->tgs_fuzzy
 162+ (int)$row->tgs_total,
 163+ (int)$row->tgs_translated,
 164+ (int)$row->tgs_fuzzy
165165 );
166166 }
167167
Index: trunk/extensions/Translate/Translate.i18n.php
@@ -48,8 +48,6 @@
4949 'translate-page-settings-legend' => 'Settings',
5050 'translate-page-task' => 'I want to',
5151 'translate-page-group' => 'Group',
52 - 'translate-page-group-tooltip' => 'The name of a group of messages.
53 -If the group consists of a subset or superset of messages from other groups, it is displayed in bold.',
5452 'translate-page-language' => 'Language',
5553 'translate-page-limit' => 'Limit',
5654 'translate-page-limit-option' => '$1 {{PLURAL:$1|message|messages}} per page',
@@ -182,6 +180,7 @@
183181 'translate-statsf-width' => 'Width in pixels:',
184182 'translate-statsf-height' => 'Height in pixels:',
185183 'translate-statsf-days' => 'Time period in days:',
 184+ 'translate-statsf-days' => 'Starting date:',
186185 'translate-statsf-scale' => 'Granularity:',
187186 'translate-statsf-scale-months' => 'Months',
188187 'translate-statsf-scale-weeks' => 'Weeks',
@@ -228,24 +227,33 @@
229228 'translate-language-code' => 'Language code',
230229 'translate-language-code-field-name' => 'Language code:',
231230 'translate-suppress-complete' => 'Suppress completely translated message groups',
 231+ 'translate-ls-noempty' => 'Suppress completely untranslated message groups',
232232 'translate-language' => 'Language',
233233 'translate-total' => 'Messages',
234 - 'translate-total-tooltip' => 'The total number of messages in this message group.',
235234 'translate-untranslated' => 'Untranslated',
236 - 'translate-untranslated-tooltip' => 'The total number of untranslated messages in this message group.
237 -This includes the messages that have been tagged as outdated.',
238235 'translate-percentage-complete' => 'Completion',
239 - 'translate-percentage-complete-tooltip' => 'The percentage of messages in the group that have been translated and not marked as outdated.',
240236 'translate-percentage-fuzzy' => 'Outdated',
241 - 'translate-percentage-fuzzy-tooltip' => 'The percentage of messages in the group that have been translated and have been marked as outdated.
242 -Messages are marked outdated because the English language source message has changed, because an automated check for the message failed, or because someone marked it as outdated.',
243237 'translate-nothing-to-do' => 'All possible translations appear to have been made.
244238 You are encouraged to review messages through [[Special:Translate|{{int:translate}}]].',
245239 'translate-languagestats-groups' => ' # Add message group IDs, one per line to restrict the message groups that
246240 # are shown on Special:LanguageStats. Non-existing message group IDs will
247241 # be ignored.', # do not duplicate this message to other languages
248242 'translate-languagestats-overall' => 'All message groups together',
 243+ 'translate-ls-submit' => 'Show statistics',
 244+ 'translate-ls-column-group' => 'Message group',
249245
 246+ # Special:GroupStats
 247+ 'translate-mgs-pagename' => 'Message group statistics',
 248+ 'translate-mgs-fieldset' => 'Display preferences',
 249+ 'translate-mgs-group' => 'Message group:',
 250+ 'translate-mgs-nocomplete' => 'Do not display languages which have completed translation',
 251+ 'translate-mgs-noempty' => 'Do not display languages which do not have any translations',
 252+ 'translate-mgs-submit' => 'Show statistics',
 253+ 'translate-mgs-column-language' => 'Language',
 254+ 'translate-mgs-totals' => 'All languages together',
 255+ 'translate-mgs-invalid-group' => 'The specified group $1 does on exist.',
 256+ 'translate-mgs-nothing' => 'Nothing to show for requested statistics.',
 257+
250258 # Special:SupportedLanguages
251259 'supportedlanguages' => 'Supported languages',
252260 'supportedlanguages-summary' => 'This page displays a list of all the languages supported by {{SITENAME}}, together with the names of the translators working on that language.
Index: trunk/extensions/Translate/specials/SpecialLanguageStats.php
@@ -21,220 +21,231 @@
2222 * @ingroup SpecialPage TranslateSpecialPage Stats
2323 */
2424 class SpecialLanguageStats extends IncludableSpecialPage {
25 - protected $purge = false;
 25+ /// @var StatsTable
 26+ protected $table;
2627
 28+ /// @var String
 29+ protected $targetValueName = 'code';
 30+
 31+ /**
 32+ * Most of the displayed numbers added together.
 33+ */
 34+ protected $totals = array( 0, 0, 0 );
 35+
 36+ /**
 37+ * How long spend time calculating missing numbers, before
 38+ * bailing out.
 39+ * @var int
 40+ */
 41+ protected $timelimit = 8;
 42+
 43+ /**
 44+ * Flag to set if nothing to show.
 45+ * @var bool
 46+ */
 47+ protected $nothing = false;
 48+
 49+ /**
 50+ * Flag to set if not all numbers are available.
 51+ * @var bool
 52+ */
2753 protected $incomplete = false;
2854
29 - function __construct() {
 55+ /**
 56+ * Whether to hide rows which are fully translated.
 57+ * @var bool
 58+ */
 59+ protected $noComplete = true;
 60+
 61+ /**
 62+ * Whether to hide reos which are fully untranslated.
 63+ * @var bool
 64+ */
 65+ protected $noEmpty = false;
 66+
 67+ /**
 68+ * The target of stats, language code or group id.
 69+ */
 70+ protected $target;
 71+
 72+ public function __construct() {
3073 parent::__construct( 'LanguageStats' );
3174 }
3275
3376 function execute( $par ) {
34 - global $wgRequest, $wgOut, $wgUser;
 77+ global $wgRequest, $wgOut;
3578
36 - $this->purge = $wgRequest->getVal( 'action' ) === 'purge';
37 - $this->linker = $wgUser->getSkin();
38 - $this->translate = SpecialPage::getTitleFor( 'Translate' );
 79+ $request = $wgRequest;
3980
 81+ $this->purge = $request->getVal( 'action' ) === 'purge';
 82+ $this->table = new StatsTable();
 83+
4084 $this->setHeaders();
4185 $this->outputHeader();
4286
4387 $wgOut->addModules( 'ext.translate.special.languagestats' );
4488 $wgOut->addModules( 'ext.translate.messagetable' );
4589
46 - // no UI when including()
 90+ $params = explode( '/', $par );
 91+ if ( isset( $params[0] ) ) {
 92+ $this->target = $params[0];
 93+ }
 94+ if ( isset( $params[1] ) ) {
 95+ $this->noComplete = (bool)$params[1];
 96+ }
 97+ if ( isset( $params[2] ) ) {
 98+ $this->noEmpty = (bool)$params[2];
 99+ }
 100+
 101+ // Whether the form has been submitted
 102+ $submitted = $request->getVal( 'x' ) === 'D';
 103+
 104+ // Default booleans to false if the form was submitted
47105 if ( !$this->including() ) {
48 - $code = $wgRequest->getVal( 'code', $par );
49 - $suppressComplete = $wgRequest->getVal( 'suppresscomplete', $par );
50 - $wgOut->addHTML( $this->buildLanguageForm( $code, $suppressComplete ) );
51 - } else {
52 - $paramArray = explode( '/', $par, 2 );
53 - $code = $paramArray[0];
54 - $suppressComplete = isset( $paramArray[1] ) && (bool)$paramArray[1];
 106+ $this->target = $request->getVal( $this->targetValueName, $this->target );
 107+ $this->noComplete = $request->getBool( 'suppresscomplete', $this->noComplete && !$submitted );
 108+ $this->noEmpty = $request->getBool( 'suppressempty', $this->noEmpty && !$submitted );
55109 }
56110
57 - if ( !$code ) {
58 - if ( $wgUser->isLoggedIn() ) {
59 - global $wgLang;
60 - $code = $wgLang->getCode();
61 - }
 111+ if ( !$this->including() ) {
 112+ $wgOut->addHTML( $this->getForm() );
62113 }
63114
64 - $out = '';
65 -
66 - if ( array_key_exists( $code, Language::getLanguageNames() ) ) {
67 - $out .= $this->getGroupStats( $code, $suppressComplete );
 115+ $allowedValues = $this->getAllowedValues();
 116+ if ( in_array( $this->target, $allowedValues, true ) ) {
 117+ $this->outputIntroduction();
 118+ $output = $this->getTable();
68119 if ( $this->incomplete ) {
69120 $wgOut->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-langstats-incomplete' );
70121 }
71 - } elseif ( $code ) {
72 - $wgOut->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-page-no-such-language' );
 122+ if ( $this->nothing ) {
 123+ $wgOut->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-mgs-nothing' );
 124+ }
 125+ $wgOut->addHTML( $output );
 126+ } elseif ( $target !== null ) {
 127+ $this->invalidTarget();
73128 }
74129
75 - $wgOut->addHTML( $out );
76130 }
77131
78132 /**
 133+ * Return the list of allowed values for target here.
 134+ * @return array
 135+ */
 136+ protected function getAllowedValues() {
 137+ $langs = Language::getLanguageNames( false );
 138+ return array_keys( $langs );
 139+ }
 140+
 141+ /// Called when the target is unknown.
 142+ protected function invalidTarget() {
 143+ global $wgOut;
 144+ $wgOut->wrapWikiMsg( "<div class='error'>$1</div>", 'translate-page-no-such-language' );
 145+ }
 146+
 147+ /**
79148 * HTML for the top form.
80 - * @param $code \string A language code (default empty, example: 'en').
81 - * @param $suppressComplete \bool If completely translated groups should be suppressed (default: false).
82149 * @return \string HTML
 150+ * @todo duplicated code
83151 */
84 - function buildLanguageForm( $code = '', $suppressComplete = false ) {
 152+ protected function getForm() {
85153 global $wgScript;
86154
87 - $t = $this->getTitle();
88 -
89 - $out = Html::openElement( 'div', array( 'class' => 'languagecode' ) );
 155+ $out = Html::openElement( 'div' );
90156 $out .= Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
91 - $out .= Html::hidden( 'title', $t->getPrefixedText() );
 157+ $out .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() );
 158+ $out .= Html::hidden( 'x', 'D' ); // To detect submission
92159 $out .= Html::openElement( 'fieldset' );
93160 $out .= Html::element( 'legend', null, wfMsg( 'translate-language-code' ) );
94 - $out .= Html::openElement( 'table', array( 'id' => 'langcodeselect', 'class' => 'allpages' ) );
 161+ $out .= Html::openElement( 'table' );
95162
96163 $out .= Html::openElement( 'tr' );
97164 $out .= Html::openElement( 'td', array( 'class' => 'mw-label' ) );
98165 $out .= Xml::label( wfMsg( 'translate-language-code-field-name' ), 'code' );
99 - $out .= Xml::closeElement( 'td' );
 166+ $out .= Html::closeElement( 'td' );
100167 $out .= Html::openElement( 'td', array( 'class' => 'mw-input' ) );
101 - $out .= Xml::input( 'code', 30, str_replace( '_', ' ', $code ), array( 'id' => 'code' ) );
102 - $out .= Xml::closeElement( 'td' );
103 - $out .= Xml::closeElement( 'tr' );
 168+ $out .= Xml::input( 'code', 10, $this->target, array( 'id' => 'code' ) );
 169+ $out .= Html::closeElement( 'td' );
 170+ $out .= Html::closeElement( 'tr' );
104171
105172 $out .= Html::openElement( 'tr' );
106173 $out .= Html::openElement( 'td', array( 'colspan' => 2 ) );
107 - $out .= Xml::checkLabel( wfMsg( 'translate-suppress-complete' ), 'suppresscomplete', 'suppresscomplete', $suppressComplete );
108 - $out .= Xml::closeElement( 'td' );
109 - $out .= Xml::closeElement( 'tr' );
 174+ $out .= Xml::checkLabel( wfMsg( 'translate-suppress-complete' ), 'suppresscomplete', 'suppresscomplete', $this->noComplete );
 175+ $out .= Html::closeElement( 'td' );
 176+ $out .= Html::closeElement( 'tr' );
110177
111178 $out .= Html::openElement( 'tr' );
 179+ $out .= Html::openElement( 'td', array( 'colspan' => 2 ) );
 180+ $out .= Xml::checkLabel( wfMsg( 'translate-ls-noempty' ), 'suppressempty', 'suppressempty', $this->noEmpty );
 181+ $out .= Html::closeElement( 'td' );
 182+ $out .= Html::closeElement( 'tr' );
 183+
 184+ $out .= Html::openElement( 'tr' );
112185 $out .= Html::openElement( 'td', array( 'class' => 'mw-input', 'colspan' => 2 ) );
113 - $out .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
114 - $out .= Xml::closeElement( 'td' );
115 - $out .= Xml::closeElement( 'tr' );
 186+ $out .= Xml::submitButton( wfMsg( 'translate-ls-submit' ) );
 187+ $out .= Html::closeElement( 'td' );
 188+ $out .= Html::closeElement( 'tr' );
116189
117 - $out .= Xml::closeElement( 'table' );
118 - $out .= Xml::closeElement( 'fieldset' );
119 - $out .= Xml::closeElement( 'form' );
120 - $out .= Xml::closeElement( 'div' );
 190+ $out .= Html::closeElement( 'table' );
 191+ $out .= Html::closeElement( 'fieldset' );
 192+ $out .= Html::closeElement( 'form' );
 193+ $out .= Html::closeElement( 'div' );
121194
122195 return $out;
123196 }
124197
125198 /**
126 - * Statistics table element (heading or regular cell)
127 - *
128 - * @todo document
129 - * @param $in \string Element contents.
130 - * @param $bgcolor \string Backround color in ABABAB format.
131 - * @param $sort \string Value used for sorting.
132 - * @return \string Html td element.
 199+ * Output something helpful to guide the confused user.
133200 */
134 - function element( $in, $bgcolor = '', $sort = '' ) {
135 - $attributes = array();
136 - if ( $sort ) $attributes['data-sort-value'] = $sort;
137 - if ( $bgcolor ) $attributes['style'] = "background-color: #" . $bgcolor;
 201+ protected function outputIntroduction() {
 202+ global $wgOut, $wgLang;
138203
139 - $element = Html::element( 'td', $attributes, $in );
140 - return $element;
141 - }
142 -
143 - function getBackgroundColour( $subset, $total, $fuzzy = false ) {
144 - $v = @round( 255 * $subset / $total );
145 -
146 - if ( $fuzzy ) {
147 - // Weigh fuzzy with factor 20.
148 - $v = $v * 20;
149 - if ( $v > 255 ) $v = 255;
150 - $v = 255 - $v;
151 - }
152 -
153 - if ( $v < 128 ) {
154 - // Red to Yellow
155 - $red = 'FF';
156 - $green = sprintf( '%02X', 2 * $v );
157 - } else {
158 - // Yellow to Green
159 - $red = sprintf( '%02X', 2 * ( 255 - $v ) );
160 - $green = 'FF';
161 - }
162 - $blue = '00';
163 -
164 - return $red . $green . $blue;
165 - }
166 -
167 - function createHeader( $code ) {
168 - global $wgUser, $wgLang;
169 -
170 - $languageName = TranslateUtils::getLanguageName( $code, false, $wgLang->getCode() );
171 - $rcInLangLink = $wgUser->getSkin()->link(
 204+ $linker = class_exists( 'DummyLinker' ) ? new DummyLinker : new Linker;
 205+ $languageName = TranslateUtils::getLanguageName( $this->target, false, $wgLang->getCode() );
 206+ $rcInLangLink = $linker->link(
172207 SpecialPage::getTitleFor( 'Recentchanges' ),
173208 wfMsgHtml( 'languagestats-recenttranslations' ),
174209 array(),
175210 array(
176211 'translations' => 'only',
177 - 'trailer' => "/" . $code
 212+ 'trailer' => "/" . $this->target
178213 )
179214 );
180215
181216 $out = wfMsgExt( 'languagestats-stats-for', array( 'parse', 'replaceafter' ), $languageName, $rcInLangLink );
182 -
183 - // Create table header
184 - $out .= Html::openElement(
185 - 'table',
186 - array(
187 - 'class' => "sortable wikitable mw-sp-translate-table"
188 - )
189 - );
190 -
191 - $out .= "\n\t" . Html::openElement( 'thead' );
192 - $out .= "\n\t" . Html::openElement( 'tr' );
193 - $out .= "\n\t\t" . Html::element( 'th', array( 'title' => self::newlineToWordSeparator( wfMsg( 'translate-page-group-tooltip' ) ) ), wfMsg( 'translate-page-group' ) );
194 - $out .= "\n\t\t" . Html::element( 'th', array( 'title' => self::newlineToWordSeparator( wfMsg( 'translate-total-tooltip' ) ) ), wfMsg( 'translate-total' ) );
195 - $out .= "\n\t\t" . Html::element( 'th', array( 'title' => self::newlineToWordSeparator( wfMsg( 'translate-untranslated-tooltip' ) ) ), wfMsg( 'translate-untranslated' ) );
196 - $out .= "\n\t\t" . Html::element( 'th', array( 'title' => self::newlineToWordSeparator( wfMsg( 'translate-percentage-complete-tooltip' ) ) ), wfMsg( 'translate-percentage-complete' ) );
197 - $out .= "\n\t\t" . Html::element( 'th', array( 'title' => self::newlineToWordSeparator( wfMsg( 'translate-percentage-fuzzy-tooltip' ) ) ), wfMsg( 'translate-percentage-fuzzy' ) );
198 - $out .= "\n\t" . Xml::closeElement( 'tr' );
199 - $out .= "\n\t" . Xml::closeElement( 'thead' );
200 - $out .= "\n\t" . Html::openElement( 'tbody' );
201 -
202 - return $out;
 217+ $wgOut->addHTML( $out );
203218 }
204219
205220 /**
206 - * HTML for language statistics.
207 - * Copied and adaped from groupStatistics.php by Nikerabbit
208 - *
209 - * @param $code \string A language code (default empty, example: 'en').
210 - * @param $suppressComplete \bool If completely translated groups should be suppressed
 221+ * Returns the table itself.
211222 * @return \string HTML
212223 */
213 - function getGroupStats( $code, $suppressComplete = false ) {
214 - $this->code = $code;
215 - $this->suppressComplete = $suppressComplete;
216 - $this->totals = array( 0, 0, 0 );
217 -
 224+ function getTable() {
 225+ $table = $this->table;
218226 $out = '';
219227
220228 if ( $this->purge ) {
221 - MessageGroupStats::clearLanguage( $code );
 229+ MessageGroupStats::clearLanguage( $this->target );
222230 }
223231
224 - MessageGroupStats::setTimeLimit( 8 );
225 - $cache = MessageGroupStats::forLanguage( $code );
226 - $structure = MessageGroups::getGroupStructure();
 232+ MessageGroupStats::setTimeLimit( $this->timelimit );
 233+ $cache = MessageGroupStats::forLanguage( $this->target );
227234
 235+ $structure = MessageGroups::getGroupStructure();
228236 foreach ( $structure as $item ) {
229237 $out .= $this->makeGroupGroup( $item, $cache );
230238 }
231239
232240 if ( $out ) {
233 - $out = $this->createHeader( $code ) . "\n" . $out;
234 - $out .= $this->makeTotalRow( $this->totals );
235 - $out .= Xml::closeElement( 'tbody' );
236 - $out .= Xml::closeElement( 'table' );
 241+ $table->setMainColumnHeader( wfMessage( 'translate-ls-column-group' ) );
 242+ $out = $table->createHeader() . "\n" . $out;
 243+ $out .= $table->makeTotalRow( wfMessage( 'translate-languagestats-overall' ), $this->totals );
 244+ $out .= Html::closeElement( 'tbody' );
 245+ $out .= Html::closeElement( 'table' );
 246+ return $out;
237247 } else {
238 - $out = wfMsgExt( 'translate-nothing-to-do', 'parse' );
 248+ $this->nothing = true;
 249+ return '';
239250 }
240251
241252 /// @todo: Allow extra message here, once total translated volume goes
@@ -242,48 +253,8 @@
243254 /// if ( $this->totals['2'] && ( $this->totals['1'] / $this->totals['2'] ) > 0.95 ) {
244255 /// $out .= wfMessage( 'translate-somekey' );
245256 /// }
246 -
247 - return $out;
248257 }
249258
250 - protected function makeTotalRow( $numbers ) {
251 - list( $total, $translated, $fuzzy ) = $numbers;
252 - $out = "\t" . Html::openElement( 'tr' );
253 - $out .= "\n\t\t" . Html::rawElement( 'td', array(), wfMsg( 'translate-languagestats-overall' ) );
254 - $out .= $this->makeNumberColumns( $fuzzy, $translated, $total );
255 - return $out;
256 - }
257 -
258 - protected function makeNumberColumns( $fuzzy, $translated, $total ) {
259 - if ( $total === null ) {
260 - $na = "\n\t\t" . Html::element( 'td', array( 'data-sort-value' => -1 ), '...' );
261 - $nap = "\n\t\t" . $this->element( '...', 'AFAFAF', -1 );
262 - $out = $na . $na . $nap . $nap;
263 - $out .= "\n\t" . Xml::closeElement( 'tr' ) . "\n";
264 - return $out;
265 - }
266 -
267 - global $wgLang;
268 - $out = "\n\t\t" . Html::element( 'td',
269 - array( 'data-sort-value' => $total ),
270 - $wgLang->formatNum( $total ) );
271 -
272 - $out .= "\n\t\t" . Html::element( 'td',
273 - array( 'data-sort-value' => $total - $translated ),
274 - $wgLang->formatNum( $total - $translated ) );
275 -
276 - $out .= "\n\t\t" . $this->element( $this->formatPercentage( $translated / $total ),
277 - $this->getBackgroundColour( $translated, $total ),
278 - sprintf( '%1.3f', $translated / $total ) );
279 -
280 - $out .= "\n\t\t" . $this->element( $this->formatPercentage( $fuzzy / $total ),
281 - $this->getBackgroundColour( $fuzzy, $total, true ),
282 - sprintf( '%1.3f', $fuzzy / $total ) );
283 -
284 - $out .= "\n\t" . Xml::closeElement( 'tr' ) . "\n";
285 - return $out;
286 - }
287 -
288259 protected function makeGroupGroup( $item, $cache, $parent = '' ) {
289260 if ( !is_array( $item ) ) {
290261 return $this->makeGroupRow( $item, $cache, $parent === '' ? false : $parent );
@@ -300,35 +271,25 @@
301272 }
302273
303274 protected function makeGroupRow( $group, $cache, $parent = false ) {
304 - $out = '';
305 - $code = $this->code;
306 - $suppressComplete = $this->suppressComplete;
307 - $g = $group;
308 - $groupName = $g->getId();
309 - // Do not report if this group is blacklisted.
310 - $groupId = $g->getId();
311 - $blacklisted = $this->isBlacklisted( $groupId, $code );
312 -
313 - if ( $blacklisted !== null ) {
314 - return '';
 275+ if ( $this->table->isBlacklisted( $group->getId(), $this->target ) !== null ) {
 276+ return;
315277 }
316278
317 - $stats = $cache[$groupId];
318 - list( $total, $translated, $fuzzy ) = $stats;
 279+ $stats = $cache[$group->getId()];
319280 if ( !$group instanceof AggregateMessageGroup ) {
320281 $this->totals = MessageGroupStats::multiAdd( $this->totals, $stats );
321282 }
322 - if ( $total !== null ) {
323283
324 -
325 - if ( $total == 0 ) {
326 - $zero = serialize( $total );
327 - error_log( __METHOD__ . ": Group $groupName has zero message ($code): $zero" );
 284+ list( $total, $translated, $fuzzy ) = $stats;
 285+ if ( $total === null ) {
 286+ $this->incomplete = true;
 287+ $extra = array();
 288+ } else {
 289+ if ( $this->noComplete && $fuzzy === 0 && $translated === $total ) {
328290 return '';
329291 }
330292
331 - // Skip if $suppressComplete and complete
332 - if ( $suppressComplete && !$fuzzy && $translated === $total ) {
 293+ if ( $this->noEmpty && $translated === 0 && $fuzzy === 0 ) {
333294 return '';
334295 }
335296
@@ -337,125 +298,20 @@
338299 } else {
339300 $extra = array();
340301 }
341 - } else {
342 - $extra = array();
343 - $this->incomplete = true;
344302 }
345303
346 -
347304 $rowParams = array();
348 - $rowParams['data-groupid'] = $groupId;
 305+ $rowParams['data-groupid'] = $group->getId();
349306 if ( is_string( $parent ) ) {
350307 $rowParams['data-parentgroups'] = $parent;
351308 } elseif ( $parent === true ) {
352309 $rowParams['data-ismeta'] = '1';
353310 }
354311
355 - $out .= "\t" . Html::openElement( 'tr', $rowParams );
356 - $out .= "\n\t\t" . Html::rawElement( 'td', array(), $this->makeGroupLink( $g, $code, $extra ) );
357 - $out .= $this->makeNumberColumns( $fuzzy, $translated, $total );
358 -
 312+ $out = "\t" . Html::openElement( 'tr', $rowParams );
 313+ $out .= "\n\t\t" . Html::rawElement( 'td', array(),
 314+ $this->table->makeGroupLink( $group, $this->target, $extra ) );
 315+ $out .= $this->table->makeNumberColumns( $fuzzy, $translated, $total );
359316 return $out;
360317 }
361 -
362 - protected function formatPercentage( $num ) {
363 - global $wgLang;
364 - $fmt = $wgLang->formatNum( number_format( round( 100 * $num, 2 ), 2 ) );
365 - return wfMsg( 'percent', $fmt );
366 - }
367 -
368 - protected function getGroupLabel( $group ) {
369 - $groupLabel = htmlspecialchars( $group->getLabel() );
370 -
371 - // Bold for meta groups.
372 - if ( $group->isMeta() ) {
373 - $groupLabel = Html::rawElement( 'b', null, $groupLabel );
374 - }
375 -
376 - return $groupLabel;
377 - }
378 -
379 - protected function makeGroupLink( $group, $code, $params ) {
380 -
381 - $queryParameters = $params + array(
382 - 'group' => $group->getId(),
383 - 'language' => $code
384 - );
385 -
386 - $attributes = array(
387 - 'title' => $this->getGroupDescription( $group )
388 - );
389 -
390 - $translateGroupLink = $this->linker->link(
391 - $this->translate, $this->getGroupLabel( $group ), $attributes, $queryParameters
392 - );
393 -
394 - return $translateGroupLink;
395 - }
396 -
397 - protected function getGroupDescription( $group ) {
398 - global $wgLang;
399 - $code = $wgLang->getCode();
400 -
401 - $cache = wfGetCache( CACHE_ANYTHING );
402 - $key = wfMemckey( "translate-groupdesc-$code-" . $group->getId() );
403 - $desc = $cache->get( $key );
404 - if ( is_string( $desc ) ) {
405 - return $desc;
406 - }
407 -
408 - global $wgLang;
409 -
410 - $realFunction = array( 'MessageCache', 'singleton' );
411 - if ( is_callable( $realFunction ) ) {
412 - $mc = MessageCache::singleton();
413 - } else {
414 - global $wgMessageCache;
415 - $mc = $wgMessageCache;
416 - }
417 - $desc = $mc->transform( $group->getDescription(), true, $wgLang, $this->getTitle() );
418 - $cache->set( $key, $desc );
419 - return $desc;
420 - }
421 -
422 - protected function isBlacklisted( $groupId, $code ) {
423 - global $wgTranslateBlacklist;
424 -
425 - $blacklisted = null;
426 -
427 - $checks = array(
428 - $groupId,
429 - strtok( $groupId, '-' ),
430 - '*'
431 - );
432 -
433 - foreach ( $checks as $check ) {
434 - $blacklisted = @$wgTranslateBlacklist[$check][$code];
435 -
436 - if ( $blacklisted !== null ) {
437 - break;
438 - }
439 - }
440 -
441 - return $blacklisted;
442 - }
443 -
444 - /**
445 - * Used to circumvent ugly tooltips when newlines are used in the
446 - * message content ("x\ny" becomes "x y").
447 - *
448 - * @todo Name does not match behaviour.
449 - * @todo Make this a static helper function somewhere?
450 - */
451 - private static function newlineToWordSeparator( $text ) {
452 - $wordSeparator = wfMsg( 'word-separator' );
453 -
454 - $text = strtr( $text, array(
455 - "\n" => $wordSeparator,
456 - "\r" => $wordSeparator,
457 - "\t" => $wordSeparator,
458 - ) );
459 -
460 - return $text;
461 - }
462318 }
Index: trunk/extensions/Translate/Translate.alias.php
@@ -16,6 +16,7 @@
1717 'TranslationStats' => array( 'TranslationStats', 'TranslationStatistics' ),
1818 'Translations' => array( 'Translations' ),
1919 'LanguageStats' => array( 'LanguageStats' ),
 20+ 'MessageGroupStats' => array( 'MessageGroupStats' ),
2021 'PageTranslation' => array( 'PageTranslation' ),
2122 'ImportTranslations' => array( 'ImportTranslations' ),
2223 'ManageMessageGroups' => array( 'ManageMessageGroups' ),

Follow-up revisions

RevisionCommit summaryAuthorDate
r97261Actually add the file, missing from r97260nikerabbit13:15, 16 September 2011
r97262First bugfix to r97260nikerabbit13:18, 16 September 2011
r97267There is no getTitle in this class, ping r97260nikerabbit13:41, 16 September 2011
r97379Fixed a regression in Special:LanguageStats, default value should be the curr...nikerabbit16:17, 17 September 2011

Comments

#Comment by Mormegil (talk | contribs)   15:28, 17 September 2011

It seems the refactoring removed the default code being set to the user language, so the user is now required to enter the language code manually. If that was not intentional, I believe the feature should return.

-		if ( !$code ) {
-			if ( $wgUser->isLoggedIn() ) {
-				global $wgLang;
-				$code = $wgLang->getCode();
-			}

(See also translatewiki:Thread:Support/Translation tool sidebar link.)

Status & tagging log