class languages {
private $mList = array();
private $mMessages = array();
+ private $mTranslatableMessages = array();
private $mIgnoredMessages = array(
'sidebar',
'addsection',
'loginend',
'loginlanguagelinks',
'markaspatrolledlink',
- 'metadata-fields', // Objection, contains help
'newarticletextanon',
'noarticletextanon',
'number_of_watching_users_RCview',
require( $filename );
if ( isset( $messages ) ) {
$this->mMessages[$code] = $messages;
+ if ( $code == 'en' ) {
+ $this->mTranslatableMessages = $this->mMessages['en'];
+ foreach ( array_keys( $this->mTranslatableMessages ) as $key ) {
+ if ( in_array( $key, $this->mIgnoredMessages ) ) {
+ unset( $this->mTranslatableMessages[$key] );
+ }
+ }
+ }
} else {
$this->mMessages[$code] = array();
}
}
/**
- * Get all the messages of all the languages.
+ * Get all the messages which are translatable - not ignored messages.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The messages in this language.
+ */
+ public function getTranslatableMessages() {
+ $this->loadMessages( 'en' );
+ return $this->mTranslatableMessages;
+ }
+
+ /**
+ * Get the translated messages for a specific language.
+ *
+ * @param $code The langauge code.
+ *
+ * @return The translated messages for this language.
*/
- public function getAllMessages() {
- foreach ( $this->mList as $code ) {
- $this->getMessages( $code );
+ public function getTranslatedMessages( $code ) {
+ $this->loadMessages( 'en' );
+ $this->loadMessages( $code );
+ $translatedMessages = array();
+ foreach ( $this->mTranslatableMessages as $key => $value ) {
+ if ( isset( $this->mMessages[$code][$key] ) ) {
+ $translatedMessages[$key] = $value;
+ }
}
+ return $translatedMessages;
}
/**
$this->loadMessages( 'en' );
$this->loadMessages( $code );
$untranslatedMessages = array();
- foreach ( $this->mMessages['en'] as $key => $value ) {
- if ( !isset( $this->mMessages[$code][$key] ) && !in_array( $key, $this->mIgnoredMessages ) ) {
+ foreach ( $this->mTranslatableMessages as $key => $value ) {
+ if ( !isset( $this->mMessages[$code][$key] ) ) {
$untranslatedMessages[$key] = $value;
}
}
$this->loadMessages( $code );
$duplicateMessages = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( @$this->mMessages['en'][$key] == $value ) {
+ if ( @$this->mTranslatableMessages[$key] == $value ) {
$duplicateMessages[$key] = $value;
}
}
$this->loadMessages( $code );
$obsoleteMessages = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( !isset( $this->mMessages['en'][$key] ) ) {
+ if ( !isset( $this->mTranslatableMessages[$key] ) ) {
$obsoleteMessages[$key] = $value;
}
}
$variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
$messagesWithoutVariables = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( isset( $this->mMessages['en'][$key] ) ) {
+ if ( isset( $this->mTranslatableMessages[$key] ) ) {
$missing = array();
foreach ( $variables as $var ) {
- if ( preg_match( "/$var/sU", $this->mMessages['en'][$key] ) &&
+ if ( preg_match( "/$var/sU", $this->mTranslatableMessages[$key] ) &&
!preg_match( "/$var/sU", $value ) ) {
$missing[] = str_replace( '\$', '$', $var );
}
$this->loadMessages( $code );
$emptyMessages = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( isset( $this->mMessages['en'][$key] ) &&
+ if ( isset( $this->mTranslatableMessages[$key] ) &&
( $this->mMessages[$code][$key] === '' || $this->mMessages[$code][$key] === '-' ) ) {
$emptyMessages[$key] = $value;
}
$this->loadMessages( $code );
$messagesWithWhitespace = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( isset( $this->mMessages['en'][$key] ) && $this->mMessages['en'][$key] !== '' &&
+ if ( isset( $this->mTranslatableMessages[$key] ) && $this->mTranslatableMessages[$key] !== '' &&
$value !== rtrim( $value ) ) {
$messagesWithWhitespace[$key] = $value;
}
$wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
$nonXHTMLMessages = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( isset( $this->mMessages['en'][$key] ) && preg_match( $wrongPhrases, $value ) ) {
+ if ( isset( $this->mTranslatableMessages[$key] ) && preg_match( $wrongPhrases, $value ) ) {
$nonXHTMLMessages[$key] = $value;
}
}
$wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
$nonXHTMLMessages = array();
foreach ( $this->mMessages[$code] as $key => $value ) {
- if ( isset( $this->mMessages['en'][$key] ) && preg_match( $wrongRegExp, $value ) ) {
+ if ( isset( $this->mTranslatableMessages[$key] ) && preg_match( $wrongRegExp, $value ) ) {
foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
$value = str_replace( $hiddenChar, $viewableChar, $value );
}
<?php
/**
+ * Statistics about the localisation.
+ *
* @package MediaWiki
* @subpackage Maintenance
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @author Ashar Voultoiz <thoane@altern.org>
- * @bug 2499
*
* Output is posted from time to time on:
* http://meta.wikimedia.org/wiki/Localization_statistics
*/
-/** */
-require_once('commandLine.inc');
-require_once('languages.inc');
-
-if( isset($options['help']) ) { usage(); wfDie(); }
-// default output is WikiText
-if( !isset($options['output']) ) { $options['output']='wiki'; }
+require_once( 'commandLine.inc' );
+require_once( 'languages.inc' );
+if ( isset( $options['help'] ) ) {
+ showUsage();
+}
+# Default output is WikiText
+if ( !isset( $options['output'] ) ) {
+ $options['output'] = 'wiki';
+}
/** Print a usage message*/
-function usage() {
-print <<<END
-Usage: php transstat.php [--help] [--output:csv|text|wiki] [--showdupes]
- --help : this helpful message
- --showold : show old messages that are not in Messages.php
- --output : select an output engine one of:
- * 'csv' : Comma Separated Values.
- * 'none' : Nothing, usefull with --showdupes
- * 'wiki' : MediaWiki syntax (default).
- * 'text' : Text with tabs.
-Example: php transstat.php --showdupes --output=none
-
+function showUsage() {
+ print <<<END
+Usage: php transstat.php [--help] [--output=csv|text|wiki]
+ --help : this helpful message
+ --output : select an output engine one of:
+ * 'csv' : Comma Separated Values.
+ * 'wiki' : MediaWiki syntax (default).
+ * 'text' : Text with tabs.
+Example: php maintenance/transstat.php --output=text
END;
+ exit();
}
-
/** A general output object. Need to be overriden */
class statsOutput {
- var $output; // buffer that contain the text
- function statsOutput() { $this->output='';}
- function getContent() { return $this->output;}
-
- function formatPercent($subset, $total, $revert=false, $accuracy=2) {
+ function formatPercent( $subset, $total, $revert = false, $accuracy = 2 ) {
return @sprintf( '%.' . $accuracy . 'f%%', 100 * $subset / $total );
}
- // Override the next methods
- function heading() {}
- function footer() {}
- function blockstart() {}
- function blockend() {}
- function element($in, $heading=false) {}
-}
-
-/** Outputs nothing ! */
-class noneStatsOutput extends statsOutput {
- function getContent() { return NULL;}
+ # Override the following methods
+ function heading() {
+ }
+ function footer() {
+ }
+ function blockstart() {
+ }
+ function blockend() {
+ }
+ function element( $in, $heading = false ) {
+ }
}
/** Outputs WikiText */
class wikiStatsOutput extends statsOutput {
function heading() {
- $this->output .= "{| border=2 cellpadding=4 cellspacing=0 style=\"background: #f9f9f9; border: 1px #aaa solid; border-collapse: collapse;\" width=100%\n";
+ echo "'''Note:''' These statistics can be generated by running <code>php maintenance/transstat.php</code>.\n\n";
+ echo "{| border=2 cellpadding=4 cellspacing=0 style=\"background: #f9f9f9; border: 1px #aaa solid; border-collapse: collapse;\" width=100%\n";
+ }
+ function footer() {
+ echo "|}\n";
}
- function footer() { $this->output .= "|}\n"; }
- function blockstart() { $this->output .= "|-\n"; }
- function blockend() { $this->output .= ''; }
- function element($in, $heading = false) {
- $this->output .= ($heading ? '!' : '|') . " $in\n";
+ function blockstart() {
+ echo "|-\n";
}
- function formatPercent($subset, $total, $revert=false, $accuracy=2) {
+ function blockend() {
+ echo '';
+ }
+ function element( $in, $heading = false ) {
+ echo ($heading ? '!' : '|') . " $in\n";
+ }
+ function formatPercent( $subset, $total, $revert = false, $accuracy = 2 ) {
$v = @round(255 * $subset / $total);
- if($revert) $v = 255 - $v;
- if($v < 128) {
- // red to yellow
+ if ( $revert ) {
+ $v = 255 - $v;
+ }
+ if ( $v < 128 ) {
+ # Red to Yellow
$red = 'FF';
- $green = sprintf('%02X', 2*$v);
+ $green = sprintf( '%02X', 2 * $v );
} else {
- // yellow to green
- $red = sprintf('%02X', 2*(255 -$v) );
+ # Yellow to Green
+ $red = sprintf('%02X', 2 * ( 255 - $v ) );
$green = 'FF';
}
- $blue = '00';
- $color = $red.$green.$blue;
+ $blue = '00';
+ $color = $red . $green . $blue;
- $percent = statsOutput::formatPercent($subset, $total, $revert, $accuracy);
- return 'bgcolor="#'.$color.'" | '.$percent;
+ $percent = statsOutput::formatPercent( $subset, $total, $revert, $accuracy );
+ return 'bgcolor="#'. $color .'" | '. $percent;
}
}
/** Output text. To be used on a terminal for example. */
class textStatsOutput extends statsOutput {
- function element($in, $heading = false) {
- $this->output .= $in."\t";
+ function element( $in, $heading = false ) {
+ echo $in."\t";
+ }
+ function blockend() {
+ echo "\n";
}
- function blockend(){ $this->output .="\n";}
}
/** csv output. Some people love excel */
class csvStatsOutput extends statsOutput {
- function element($in, $heading = false) {
- $this->output .= $in.";";
+ function element( $in, $heading = false ) {
+ echo $in . ";";
}
- function blockend(){ $this->output .="\n";}
-}
-
-
-function redundant(&$arr, $langcode) {
- $redundant = 0;
- $englishMessages = Language::getMessagesFor( 'en' );
- foreach(array_keys($arr) as $key) {
- if ( !isset( $englishMessages[$key] ) ) {
- global $options;
- if( isset( $options['showold'] ) ) {
- print "Deprecated [$langcode]: $key\n";
- }
- ++$redundant;
- }
+ function blockend() {
+ echo "\n";
}
- return $redundant;
}
-// Select an output engine
-switch ($options['output']) {
- case 'csv':
- $out = new csvStatsOutput(); break;
- case 'none':
- $out = new noneStatsOutput(); break;
- case 'text':
- $out = new textStatsOutput(); break;
+# Select an output engine
+switch ( $options['output'] ) {
case 'wiki':
- $out = new wikiStatsOutput(); break;
+ $wgOut = new wikiStatsOutput();
+ break;
+ case 'text':
+ $wgOut = new textStatsOutput();
+ break;
+ case 'csv':
+ $wgOut = new csvStatsOutput();
+ break;
default:
- usage(); wfDie();
- break;
+ showUsage();
}
-$langTool = new languages();
-
-// Load message and compute stuff
-$msgs = array();
-foreach ( $langTool->getList() as $langcode ) {
- // Since they aren't loaded by default..
- require( Language::getFileName( "$IP/languages/Messages", $langcode, ".php" ) );
- if( isset( $messages ) ) {
- $msgs[$wgContLang->lcfirst($langcode)] = array(
- 'total' => count( $messages ),
- 'redundant' => redundant( $messages, $langcode ),
- );
- } else {
- $msgs[$wgContLang->lcfirst($langcode)] = array(
- 'total' => 0,
- 'redundant' => 0,
- );
- }
- unset( $messages );
-}
+# Languages
+$wgLanguages = new languages();
+
+# Header
+$wgOut->heading();
+$wgOut->blockstart();
+$wgOut->element( 'Language', true );
+$wgOut->element( 'Translated', true );
+$wgOut->element( '%', true );
+$wgOut->element( 'Possibly untranslated', true );
+$wgOut->element( '%', true );
+$wgOut->element( 'Obsolete', true );
+$wgOut->element( '%', true );
+$wgOut->blockend();
+
+foreach ( $wgLanguages->getList() as $code ) {
+ # Don't check English
+ if ( $code == 'en' ) {
+ continue;
+ }
+
+ # FIXME - temporary hack for this non-language won't appear
+ if ( $code == 'enRTL' ) {
+ continue;
+ }
-// Top entry
-$out->heading();
-$out->blockstart();
-$out->element('Language', true);
-$out->element('Translated', true);
-$out->element('%', true);
-$out->element('Untranslated', true);
-$out->element('%', true);
-$out->element('Redundant', true);
-$out->element('%', true);
-$out->blockend();
-
-// Generate rows
-foreach($msgs as $lang => $stats) {
- $out->blockstart();
- // Language
- $out->element($wgContLang->getLanguageName(strtr($lang, '_', '-')) . " ($lang)");
- // Translated
- $out->element($stats['total'] . '/' . $msgs['en']['total']);
- // % Translated
- $out->element($out->formatPercent($stats['total'], $msgs['en']['total']));
- // Untranslated
- $out->element($msgs['en']['total'] - $stats['total']);
- // % Untranslated
- $out->element($out->formatPercent($msgs['en']['total'] - $stats['total'], $msgs['en']['total'], true));
- // Redundant & % Redundant
- if($stats['redundant'] =='NC') {
- $out->element('NC');
- $out->element('NC');
- } else {
- $out->element($stats['redundant'] . '/' . $stats['total']);
- $out->element($out->formatPercent($stats['redundant'], $stats['total'],true));
- }
- $out->blockend();
+ # Calculate the numbers
+ $name = $wgLang->getLanguageName( $code );
+ $translatableMessagesNumber = count( $wgLanguages->getTranslatableMessages() );
+ $localMessagesNumber = count( $wgLanguages->getMessagesFor( $code ) );
+ $translatedMessagesNumber = count( $wgLanguages->getTranslatedMessages( $code ) );
+ $translatedMessagesPercent = $wgOut->formatPercent( $translatedMessagesNumber, $translatableMessagesNumber );
+ $duplicateMessagesNumber = count( $wgLanguages->getDuplicateMessages( $code ) );
+ $duplicateMessagesPercent = $wgOut->formatPercent( $duplicateMessagesNumber, $translatedMessagesNumber, true );
+ $obsoleteMessagesNumber = count( $wgLanguages->getObsoleteMessages( $code ) );
+ $obsoleteMessagesPercent = $wgOut->formatPercent( $obsoleteMessagesNumber, $translatedMessagesNumber, true );
+
+ # Output them
+ $wgOut->blockstart();
+ $wgOut->element( "$name ($code)" );
+ $wgOut->element( "$translatedMessagesNumber/$translatableMessagesNumber" );
+ $wgOut->element( $translatedMessagesPercent );
+ $wgOut->element( "$duplicateMessagesNumber/$translatedMessagesNumber" );
+ $wgOut->element( $duplicateMessagesPercent );
+ $wgOut->element( "$obsoleteMessagesNumber/$translatedMessagesNumber" );
+ $wgOut->element( $obsoleteMessagesPercent );
+ $wgOut->blockend();
}
-$out->footer();
-// Final output
-echo $out->getContent();
+# Footer
+$wgOut->footer();
+
?>