Rewriting the language scripts, to introduce the concept of optional messages: messag...
authorRotem Liss <rotem@users.mediawiki.org>
Fri, 22 Sep 2006 12:18:03 +0000 (12:18 +0000)
committerRotem Liss <rotem@users.mediawiki.org>
Fri, 22 Sep 2006 12:18:03 +0000 (12:18 +0000)
maintenance/checkLanguage.php
maintenance/lang2po.php
maintenance/langmemusage.php
maintenance/languages.inc
maintenance/transstat.php

index f7c5c4f..8e489b7 100644 (file)
@@ -15,11 +15,11 @@ require_once( 'languages.inc' );
  * @param $code The language code.
  */
 function checkLanguage( $code ) {
-       global $wgLanguages, $wgDisplayLevel, $wgLinks, $wgWikiLanguage, $wgChecks;
+       global $wgLanguages, $wgGeneralMessages, $wgRequiredMessagesNumber, $wgDisplayLevel, $wgLinks, $wgWikiLanguage, $wgChecks;
 
-       # Get messages number
-       $translatableMessagesNumber = count( $wgLanguages->getTranslatableMessages() );
-       $localMessagesNumber = count( $wgLanguages->getMessagesFor( $code ) );
+       # Get messages
+       $messages = $wgLanguages->getMessages( $code );
+       $messagesNumber = count( $messages['translated'] );
 
        # Skip the checks if specified
        if ( $wgDisplayLevel == 0 ) {
@@ -30,85 +30,88 @@ function checkLanguage( $code ) {
        if ( in_array( 'untranslated', $wgChecks ) ) {
                $untranslatedMessages = $wgLanguages->getUntranslatedMessages( $code );
                $untranslatedMessagesNumber = count( $untranslatedMessages );
-               $wgLanguages->outputMessagesList( $untranslatedMessages, $code, "\n$untranslatedMessagesNumber messages of $translatableMessagesNumber are not translated to $code, but exist in en:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $untranslatedMessages, $code, "\n$untranslatedMessagesNumber messages of $wgRequiredMessagesNumber are not translated to $code, but exist in en:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Duplicate messages
        if ( in_array( 'duplicate', $wgChecks ) ) {
                $duplicateMessages = $wgLanguages->getDuplicateMessages( $code );
                $duplicateMessagesNumber = count( $duplicateMessages );
-               $wgLanguages->outputMessagesList( $duplicateMessages, $code, "\n$duplicateMessagesNumber messages of $localMessagesNumber are translated the same in en and $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $duplicateMessages, $code, "\n$duplicateMessagesNumber messages of $messagesNumber are translated the same in en and $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Obsolete messages
        if ( in_array( 'obsolete', $wgChecks ) ) {
-               $obsoleteMessages = $wgLanguages->getObsoleteMessages( $code );
+               $obsoleteMessages = $messages['obsolete'];
                $obsoleteMessagesNumber = count( $obsoleteMessages );
-               $wgLanguages->outputMessagesList( $obsoleteMessages, $code, "\n$obsoleteMessagesNumber messages of $localMessagesNumber are not exist in en (or are in the ignored list), but still exist in $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $obsoleteMessages, $code, "\n$obsoleteMessagesNumber messages of $messagesNumber are not exist in en (or are in the ignored list), but still exist in $code:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Messages without variables
        if ( in_array( 'variables', $wgChecks ) ) {
                $messagesWithoutVariables = $wgLanguages->getMessagesWithoutVariables( $code );
                $messagesWithoutVariablesNumber = count( $messagesWithoutVariables );
-               $wgLanguages->outputMessagesList( $messagesWithoutVariables, $code, "\n$messagesWithoutVariablesNumber messages of $localMessagesNumber in $code don't use some variables while en uses them:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $messagesWithoutVariables, $code, "\n$messagesWithoutVariablesNumber messages of $messagesNumber in $code don't use some variables while en uses them:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Empty messages
        if ( in_array( 'empty', $wgChecks ) ) {
                $emptyMessages = $wgLanguages->getEmptyMessages( $code );
                $emptyMessagesNumber = count( $emptyMessages );
-               $wgLanguages->outputMessagesList( $emptyMessages, $code, "\n$emptyMessagesNumber messages of $localMessagesNumber in $code are empty or -:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $emptyMessages, $code, "\n$emptyMessagesNumber messages of $messagesNumber in $code are empty or -:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Messages with whitespace
        if ( in_array( 'whitespace', $wgChecks ) ) {
                $messagesWithWhitespace = $wgLanguages->getMessagesWithWhitespace( $code );
                $messagesWithWhitespaceNumber = count( $messagesWithWhitespace );
-               $wgLanguages->outputMessagesList( $messagesWithWhitespace, $code, "\n$messagesWithWhitespaceNumber messages of $localMessagesNumber in $code have a trailing whitespace:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $messagesWithWhitespace, $code, "\n$messagesWithWhitespaceNumber messages of $messagesNumber in $code have a trailing whitespace:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Non-XHTML messages
        if ( in_array( 'xhtml', $wgChecks ) ) {
                $nonXHTMLMessages = $wgLanguages->getNonXHTMLMessages( $code );
                $nonXHTMLMessagesNumber = count( $nonXHTMLMessages );
-               $wgLanguages->outputMessagesList( $nonXHTMLMessages, $code, "\n$nonXHTMLMessagesNumber messages of $localMessagesNumber in $code are not well-formed XHTML:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $nonXHTMLMessages, $code, "\n$nonXHTMLMessagesNumber messages of $messagesNumber in $code are not well-formed XHTML:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 
        # Messages with wrong characters
        if ( in_array( 'chars', $wgChecks ) ) {
                $messagesWithWrongChars = $wgLanguages->getMessagesWithWrongChars( $code );
                $messagesWithWrongCharsNumber = count( $messagesWithWrongChars );
-               $wgLanguages->outputMessagesList( $messagesWithWrongChars, $code, "\n$messagesWithWrongCharsNumber messages of $localMessagesNumber in $code include hidden chars which should not be used in the messages:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
+               $wgLanguages->outputMessagesList( $messagesWithWrongChars, $code, "\n$messagesWithWrongCharsNumber messages of $messagesNumber in $code include hidden chars which should not be used in the messages:", $wgDisplayLevel, $wgLinks, $wgWikiLanguage );
        }
 }
 
 # Show help
 if ( isset( $options['help'] ) ) {
-       echo "Run this script to check a specific language file.\n";
-       echo "Parameters:\n";
-       echo "\t* lang: Language code (default: the installation default language). You can also specify \"all\" to check all the languages.\n";
-       echo "\t* help: Show help.\n";
-       echo "\t* level: Show the following level (default: 2).\n";
-       echo "\t* links: Link the message values (default off).\n";
-       echo "\t* wikilang: For the links, what is the content language of the wiki to display the output in (default en).\n";
-       echo "\t* whitelist: Make only the following checks (form: code,code).\n";
-       echo "\t* blacklist: Don't make the following checks (form: code,code).\n";
-       echo "\t* duplicate: Additionally check for messages which are translated the same to English (default off).\n";
-       echo "\t* noexif: Don't check for EXIF messages (a bit hard and boring to translate), if you know that they are not translated and want to focus on other problems (default off).\n";
-       echo "Check codes (ideally, should be zero; all the checks are executed by default):\n";
-       echo "\t* untranslated: Messages which are translatable, but not translated.\n";
-       echo "\t* obsolete: Messages which are untranslatable, but translated.\n";
-       echo "\t* variables: Messages without variables which should be used.\n";
-       echo "\t* empty: Empty messages.\n";
-       echo "\t* whitespace: Messages which have trailing whitespace.\n";
-       echo "\t* xhtml: Messages which are not well-formed XHTML.\n";
-       echo "\t* chars: Messages with hidden characters.\n";
-       echo "Display levels (default: 2):\n";
-       echo "\t* 0: Skip the checks (useful for checking syntax).\n";
-       echo "\t* 1: Show only the stub headers and number of wrong messages, without list of messages.\n";
-       echo "\t* 2: Show only the headers and the message keys, without the message values.\n";
-       echo "\t* 3: Show both the headers and the complete messages, with both keys and values.\n";
+       echo <<<END
+Run this script to check a specific language file, or all of them.
+Parameters:
+       * lang: Language code (default: the installation default language). You can also specify "all" to check all the languages.
+       * help: Show this help.
+       * level: Show the following level (default: 2).
+       * links: Link the message values (default off).
+       * wikilang: For the links, what is the content language of the wiki to display the output in (default en).
+       * whitelist: Make only the following checks (form: code,code).
+       * blacklist: Don't make the following checks (form: code,code).
+       * duplicate: Additionally check for messages which are translated the same to English (default off).
+       * noexif: Don't check for EXIF messages (a bit hard and boring to translate), if you know that they are currently not translated and want to focus on other problems (default off).
+Check codes (ideally, all of them should result 0; all the checks are executed by default):
+       * untranslated: Messages which are required to translate, but are not translated.
+       * obsolete: Messages which are untranslatable, but translated.
+       * variables: Messages without variables which should be used.
+       * empty: Empty messages.
+       * whitespace: Messages which have trailing whitespace.
+       * xhtml: Messages which are not well-formed XHTML.
+       * chars: Messages with hidden characters.
+Display levels (default: 2):
+       * 0: Skip the checks (useful for checking syntax).
+       * 1: Show only the stub headers and number of wrong messages, without list of messages.
+       * 2: Show only the headers and the message keys, without the message values.
+       * 3: Show both the headers and the complete messages, with both keys and values.
+
+END;
        exit();
 }
 
@@ -149,9 +152,13 @@ $wgCheckEXIF = !isset( $options['noexif'] );
 # Get language objects
 $wgLanguages = new languages( $wgCheckEXIF );
 
+# Get the general messages
+$wgGeneralMessages = $wgLanguages->getGeneralMessages();
+$wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
+
 # Check the language
 if ( $wgCode == 'all' ) {
-       foreach ( $wgLanguages->getList() as $language ) {
+       foreach ( $wgLanguages->getLanguages() as $language ) {
                if ( $language != 'en' && $language != 'enRTL' ) {
                        checkLanguage( $language );
                }
index cff2a06..b344824 100644 (file)
@@ -135,7 +135,7 @@ echo "done.\n";
 $langTool = new languages();
 
 // Do all languages
-foreach ( $langTool->getList() as $langcode) {
+foreach ( $langTool->getMessages() as $langcode) {
        echo "Loading messages for $langcode:\t";
        require_once( Language::getFileName( "$IP/languages/Language", $langcode, ".php" ) );
        $arr = 'wgAllMessages'.$langcode;
index 44a48b7..82030f9 100644 (file)
@@ -17,7 +17,7 @@ $memlast = $memstart = memory_get_usage();
 
 print 'Base memory usage: '.$memstart."\n";
 
-foreach ( $langtool->getList() as $langcode ) {
+foreach ( $langtool->getLanguages() as $langcode ) {
        require_once( Language::getFileName( "$IP/languages/Language", $langcode, ".php" ) );
        $memstep = memory_get_usage();
        printf( "%12s: %d\n", $langcode, ($memstep- $memlast) );
index a83bca5..becfc51 100644 (file)
@@ -7,9 +7,10 @@
  */
 
 class languages {
-       private $mList = array();
-       private $mMessages = array();
-       private $mTranslatableMessages = array();
+       private $mLanguages; # List of languages
+       private $mRawMessages; # Raw list of the messages in each language
+       private $mMessages; # Messages in each language (except for English), divided to groups
+       private $mGeneralMessages; # General messages in English, divided to groups
        private $mIgnoredMessages = array(
                'sidebar',
                'addsection',
@@ -20,9 +21,7 @@ class languages {
                'exif-model-value',
                'exif-software-value',
                'history_copyright',
-               'imgmultigotopost',
                'licenses',
-               'linkprefix',
                'loginend',
                'loginlanguagelinks',
                'markaspatrolledlink',
@@ -42,8 +41,249 @@ class languages {
                'trackback',
                'trackbackexcerpt',
                'widthheight',
-       );
-       private $mCheckEXIF;
+       ); # All the messages which should be exist only in the English file
+       private $mOptionalMessages = array(
+               'imgmultigotopost',
+               'linkprefix',
+       ); # All the messages which may be translated or not, depending on the language
+       private $mEXIFMessages = array(
+               'exif-imagewidth',
+               'exif-imagelength',
+               'exif-bitspersample',
+               'exif-compression',
+               'exif-photometricinterpretation',
+               'exif-orientation',
+               'exif-samplesperpixel',
+               'exif-planarconfiguration',
+               'exif-ycbcrsubsampling',
+               'exif-ycbcrpositioning',
+               'exif-xresolution',
+               'exif-yresolution',
+               'exif-resolutionunit',
+               'exif-stripoffsets',
+               'exif-rowsperstrip',
+               'exif-stripbytecounts',
+               'exif-jpeginterchangeformat',
+               'exif-jpeginterchangeformatlength',
+               'exif-transferfunction',
+               'exif-whitepoint',
+               'exif-primarychromaticities',
+               'exif-ycbcrcoefficients',
+               'exif-referenceblackwhite',
+               'exif-datetime',
+               'exif-imagedescription',
+               'exif-make',
+               'exif-model',
+               'exif-software',
+               'exif-artist',
+               'exif-copyright',
+               'exif-exifversion',
+               'exif-flashpixversion',
+               'exif-colorspace',
+               'exif-componentsconfiguration',
+               'exif-compressedbitsperpixel',
+               'exif-pixelydimension',
+               'exif-pixelxdimension',
+               'exif-makernote',
+               'exif-usercomment',
+               'exif-relatedsoundfile',
+               'exif-datetimeoriginal',
+               'exif-datetimedigitized',
+               'exif-subsectime',
+               'exif-subsectimeoriginal',
+               'exif-subsectimedigitized',
+               'exif-exposuretime',
+               'exif-exposuretime-format',
+               'exif-fnumber',
+               'exif-fnumber-format',
+               'exif-exposureprogram',
+               'exif-spectralsensitivity',
+               'exif-isospeedratings',
+               'exif-oecf',
+               'exif-shutterspeedvalue',
+               'exif-aperturevalue',
+               'exif-brightnessvalue',
+               'exif-exposurebiasvalue',
+               'exif-maxaperturevalue',
+               'exif-subjectdistance',
+               'exif-meteringmode',
+               'exif-lightsource',
+               'exif-flash',
+               'exif-focallength',
+               'exif-focallength-format',
+               'exif-subjectarea',
+               'exif-flashenergy',
+               'exif-spatialfrequencyresponse',
+               'exif-focalplanexresolution',
+               'exif-focalplaneyresolution',
+               'exif-focalplaneresolutionunit',
+               'exif-subjectlocation',
+               'exif-exposureindex',
+               'exif-sensingmethod',
+               'exif-filesource',
+               'exif-scenetype',
+               'exif-cfapattern',
+               'exif-customrendered',
+               'exif-exposuremode',
+               'exif-whitebalance',
+               'exif-digitalzoomratio',
+               'exif-focallengthin35mmfilm',
+               'exif-scenecapturetype',
+               'exif-gaincontrol',
+               'exif-contrast',
+               'exif-saturation',
+               'exif-sharpness',
+               'exif-devicesettingdescription',
+               'exif-subjectdistancerange',
+               'exif-imageuniqueid',
+               'exif-gpsversionid',
+               'exif-gpslatituderef',
+               'exif-gpslatitude',
+               'exif-gpslongituderef',
+               'exif-gpslongitude',
+               'exif-gpsaltituderef',
+               'exif-gpsaltitude',
+               'exif-gpstimestamp',
+               'exif-gpssatellites',
+               'exif-gpsstatus',
+               'exif-gpsmeasuremode',
+               'exif-gpsdop',
+               'exif-gpsspeedref',
+               'exif-gpsspeed',
+               'exif-gpstrackref',
+               'exif-gpstrack',
+               'exif-gpsimgdirectionref',
+               'exif-gpsimgdirection',
+               'exif-gpsmapdatum',
+               'exif-gpsdestlatituderef',
+               'exif-gpsdestlatitude',
+               'exif-gpsdestlongituderef',
+               'exif-gpsdestlongitude',
+               'exif-gpsdestbearingref',
+               'exif-gpsdestbearing',
+               'exif-gpsdestdistanceref',
+               'exif-gpsdestdistance',
+               'exif-gpsprocessingmethod',
+               'exif-gpsareainformation',
+               'exif-gpsdatestamp',
+               'exif-gpsdifferential',
+               'exif-compression-1',
+               'exif-compression-6',
+               'exif-photometricinterpretation-2',
+               'exif-photometricinterpretation-6',
+               'exif-orientation-1',
+               'exif-orientation-2',
+               'exif-orientation-3',
+               'exif-orientation-4',
+               'exif-orientation-5',
+               'exif-orientation-6',
+               'exif-orientation-7',
+               'exif-orientation-8',
+               'exif-planarconfiguration-1',
+               'exif-planarconfiguration-2',
+               'exif-xyresolution-i',
+               'exif-xyresolution-c',
+               'exif-colorspace-1',
+               'exif-colorspace-ffff.h',
+               'exif-componentsconfiguration-0',
+               'exif-componentsconfiguration-1',
+               'exif-componentsconfiguration-2',
+               'exif-componentsconfiguration-3',
+               'exif-componentsconfiguration-4',
+               'exif-componentsconfiguration-5',
+               'exif-componentsconfiguration-6',
+               'exif-exposureprogram-0',
+               'exif-exposureprogram-1',
+               'exif-exposureprogram-2',
+               'exif-exposureprogram-3',
+               'exif-exposureprogram-4',
+               'exif-exposureprogram-5',
+               'exif-exposureprogram-6',
+               'exif-exposureprogram-7',
+               'exif-exposureprogram-8',
+               'exif-subjectdistance-value',
+               'exif-meteringmode-0',
+               'exif-meteringmode-1',
+               'exif-meteringmode-2',
+               'exif-meteringmode-3',
+               'exif-meteringmode-4',
+               'exif-meteringmode-5',
+               'exif-meteringmode-6',
+               'exif-meteringmode-255',
+               'exif-lightsource-0',
+               'exif-lightsource-1',
+               'exif-lightsource-2',
+               'exif-lightsource-3',
+               'exif-lightsource-4',
+               'exif-lightsource-9',
+               'exif-lightsource-10',
+               'exif-lightsource-11',
+               'exif-lightsource-12',
+               'exif-lightsource-13',
+               'exif-lightsource-14',
+               'exif-lightsource-15',
+               'exif-lightsource-17',
+               'exif-lightsource-18',
+               'exif-lightsource-19',
+               'exif-lightsource-20',
+               'exif-lightsource-21',
+               'exif-lightsource-22',
+               'exif-lightsource-23',
+               'exif-lightsource-24',
+               'exif-lightsource-255',
+               'exif-focalplaneresolutionunit-2',
+               'exif-sensingmethod-1',
+               'exif-sensingmethod-2',
+               'exif-sensingmethod-3',
+               'exif-sensingmethod-4',
+               'exif-sensingmethod-5',
+               'exif-sensingmethod-7',
+               'exif-sensingmethod-8',
+               'exif-filesource-3',
+               'exif-scenetype-1',
+               'exif-customrendered-0',
+               'exif-customrendered-1',
+               'exif-exposuremode-0',
+               'exif-exposuremode-1',
+               'exif-exposuremode-2',
+               'exif-whitebalance-0',
+               'exif-whitebalance-1',
+               'exif-scenecapturetype-0',
+               'exif-scenecapturetype-1',
+               'exif-scenecapturetype-2',
+               'exif-scenecapturetype-3',
+               'exif-gaincontrol-0',
+               'exif-gaincontrol-1',
+               'exif-gaincontrol-2',
+               'exif-gaincontrol-3',
+               'exif-gaincontrol-4',
+               'exif-contrast-0',
+               'exif-contrast-1',
+               'exif-contrast-2',
+               'exif-saturation-0',
+               'exif-saturation-1',
+               'exif-saturation-2',
+               'exif-sharpness-0',
+               'exif-sharpness-1',
+               'exif-sharpness-2',
+               'exif-subjectdistancerange-0',
+               'exif-subjectdistancerange-1',
+               'exif-subjectdistancerange-2',
+               'exif-subjectdistancerange-3',
+               'exif-gpslatitude-n',
+               'exif-gpslatitude-s',
+               'exif-gpslongitude-e',
+               'exif-gpslongitude-w',
+               'exif-gpsstatus-a',
+               'exif-gpsstatus-v',
+               'exif-gpsmeasuremode-2',
+               'exif-gpsmeasuremode-3',
+               'exif-gpsspeed-k',
+               'exif-gpsspeed-m',
+               'exif-gpsspeed-n',
+               'exif-gpsdirection-t',
+               'exif-gpsdirection-m',
+       ); # All the EXIF messages, may be set as optional if defined as such
 
        /**
         * Load the list of languages: all the Messages*.php
@@ -53,32 +293,34 @@ class languages {
         */
        function __construct( $exif = true ) {
                global $IP;
-               $dir = opendir("$IP/languages");
+               $dir = opendir( "$IP/languages" );
                while ( $file = readdir( $dir ) ) {
                        if ( preg_match( "/Messages([^.]*?)\.php$/", $file, $matches ) ) {
-                               $this->mList[] = str_replace( '_', '-', strtolower( substr( $matches[1], 0, 1 ) ) . substr( $matches[1], 1 ) );
+                               $this->mLanguages[] = str_replace( '_', '-', strtolower( substr( $matches[1], 0, 1 ) ) . substr( $matches[1], 1 ) );
                        }
                }
-               sort( $this->mList );
-               $this->mCheckEXIF = $exif;
+               sort( $this->mLanguages );
+               if ( !$exif ) {
+                       $this->mOptionalMessages = array_merge( $this->mOptionalMessages, $this->mEXIFMessages );
+               }
        }
 
        /**
         * Get the language list.
         *
-        * @return the language list
+        * @return The language list.
         */
-       public function getList() {
-               return $this->mList;
+       public function getLanguages() {
+               return $this->mLanguages;
        }
 
        /**
-        * Load the messages for a specific langauge from the messages file.
+        * Load the raw messages for a specific langauge from the messages file.
         *
         * @param $code The langauge code.
         */
-       private function loadMessages( $code ) {
-               if ( isset( $this->mMessages[$code] ) ) {
+       private function loadRawMessages( $code ) {
+               if ( isset( $this->mRawMessages[$code] ) ) {
                        return;
                }
                global $IP;
@@ -86,87 +328,111 @@ class languages {
                if ( file_exists( $filename ) ) {
                        require( $filename );
                        if ( isset( $messages ) ) {
-                               $this->mMessages[$code] = $messages;
-                               if ( !$this->mCheckEXIF ) {
-                                       $this->filterMessages( $code, 'exif-' );
-                               }
+                               $this->mRawMessages[$code] = $messages;
                        } else {
-                               $this->mMessages[$code] = array();
+                               $this->mRawMessages[$code] = array();
                        }
                } else {
-                       $this->mMessages[$code] = array();
+                       $this->mRawMessages[$code] = array();
                }
        }
 
        /**
-        * Load the messages in English which can be translated.
+        * Load the messages for a specific language (which is not English) and divide them to groups:
+        * all - all the messages.
+        * required - messages which should be translated in order to get a complete translation.
+        * optional - messages which can be translated, the fallback translation is used if not translated.
+        * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
+        * translated - messages which are either required or optional, but translated from English and needed.
+        *
+        * @param $code The language code.
         */
-       private function loadTranslatableMessages() {
-               $this->loadMessages( 'en' );
-               $this->mTranslatableMessages = $this->mMessages['en'];
-               foreach ( array_keys( $this->mTranslatableMessages ) as $key ) {
-                       if ( in_array( $key, $this->mIgnoredMessages ) ) {
-                               unset( $this->mTranslatableMessages[$key] );
+       private function loadMessages( $code ) {
+               if ( isset( $this->mMessages[$code] ) ) {
+                       return;
+               }
+               $this->loadRawMessages( $code );
+               $this->loadGeneralMessages();
+               $this->mMessages[$code]['all'] = $this->mRawMessages[$code];
+               $this->mMessages[$code]['required'] = array();
+               $this->mMessages[$code]['optional'] = array();
+               $this->mMessages[$code]['obsolete'] = array();
+               $this->mMessages[$code]['translated'] = array();
+               foreach ( $this->mMessages[$code]['all'] as $key => $value ) {
+                       if ( isset( $this->mGeneralMessages['required'][$key] ) ) {
+                               $this->mMessages[$code]['required'][$key] = $value;
+                               $this->mMessages[$code]['translated'][$key] = $value;
+                       } else if ( isset( $this->mGeneralMessages['optional'][$key] ) ) {
+                               $this->mMessages[$code]['optional'][$key] = $value;
+                               $this->mMessages[$code]['translated'][$key] = $value;
+                       } else {
+                               $this->mMessages[$code]['obsolete'][$key] = $value;
                        }
                }
        }
 
        /**
-        * Filter messages by string.
-        *
-        * @param $code The language code.
-        * @param $pattern Filter messages which include this string.
+        * Load the messages for English and divide them to groups:
+        * all - all the messages.
+        * required - messages which should be translated to other languages in order to get a complete translation.
+        * optional - messages which can be translated to other languages, but it's not required for a complete translation.
+        * ignored - messages which should not be translated to other languages.
+        * translatable - messages which are either required or optional, but can be translated from English.
         */
-       private function filterMessages( $code, $pattern ) {
-               foreach ( array_keys( $this->mMessages[$code] ) as $key ) {
-                       if ( strpos( $key, $pattern ) !== FALSE ) {
-                               unset( $this->mMessages[$code][$key] );
+       private function loadGeneralMessages() {
+               if ( isset( $this->mGeneralMessages ) ) {
+                       return;
+               }
+               $this->loadRawMessages( 'en' );
+               $this->mGeneralMessages['all'] = $this->mRawMessages['en'];
+               $this->mGeneralMessages['required'] = array();
+               $this->mGeneralMessages['optional'] = array();
+               $this->mGeneralMessages['ignored'] = array();
+               $this->mGeneralMessages['translatable'] = array();
+               foreach ( $this->mGeneralMessages['all'] as $key => $value ) {
+                       if ( in_array( $key, $this->mIgnoredMessages ) ) {
+                               $this->mGeneralMessages['ignored'][$key] = $value;
+                       } else if ( in_array( $key, $this->mOptionalMessages ) ) {
+                               $this->mGeneralMessages['optional'][$key] = $value;
+                               $this->mGeneralMessages['translatable'][$key] = $value;
+                       } else {
+                               $this->mGeneralMessages['required'][$key] = $value;
+                               $this->mGeneralMessages['translatable'][$key] = $value;
                        }
                }
        }
 
        /**
-        * Get all the messages for a specific langauge, without the fallback
-        * language messages.
+        * Get all the messages for a specific langauge (not English), without the
+        * fallback language messages, divided to groups:
+        * all - all the messages.
+        * required - messages which should be translated in order to get a complete translation.
+        * optional - messages which can be translated, the fallback translation is used if not translated.
+        * obsolete - messages which should not be translated, either because they are not exist, or they are ignored messages.
+        * translated - messages which are either required or optional, but translated from English and needed.
         *
         * @param $code The langauge code.
         *
         * @return The messages in this language.
         */
-       public function getMessagesFor( $code ) {
+       public function getMessages( $code ) {
                $this->loadMessages( $code );
                return $this->mMessages[$code];
        }
 
        /**
-        * 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->loadTranslatableMessages();
-               return $this->mTranslatableMessages;
-       }
-
-       /**
-        * Get the translated messages for a specific language.
+        * Get all the general English messages, divided to groups:
+        * all - all the messages.
+        * required - messages which should be translated to other languages in order to get a complete translation.
+        * optional - messages which can be translated to other languages, but it's not required for a complete translation.
+        * ignored - messages which should not be translated to other languages.
+        * translatable - messages which are either required or optional, but can be translated from English.
         *
-        * @param $code The langauge code.
-        *
-        * @return The translated messages for this language.
+        * @return The general English messages.
         */
-       public function getTranslatedMessages( $code ) {
-               $this->loadTranslatableMessages();
-               $this->loadMessages( $code );
-               $translatedMessages = array();
-               foreach ( $this->mTranslatableMessages as $key => $value ) {
-                       if ( isset( $this->mMessages[$code][$key] ) ) {
-                               $translatedMessages[$key] = $value;
-                       }
-               }
-               return $translatedMessages;
+       public function getGeneralMessages() {
+               $this->loadGeneralMessages();
+               return $this->mGeneralMessages;
        }
 
        /**
@@ -177,13 +443,13 @@ class languages {
         * @return The untranslated messages for this language.
         */
        public function getUntranslatedMessages( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
+               $requiredGeneralMessages = array_keys( $this->mGeneralMessages['required'] );
+               $requiredMessages = array_keys( $this->mMessages[$code]['required'] );
                $untranslatedMessages = array();
-               foreach ( $this->mTranslatableMessages as $key => $value ) {
-                       if ( !isset( $this->mMessages[$code][$key] ) ) {
-                               $untranslatedMessages[$key] = $value;
-                       }
+               foreach ( array_diff( $requiredGeneralMessages, $requiredMessages ) as $key ) {
+                       $untranslatedMessages[$key] = $this->mGeneralMessages['required'][$key];
                }
                return $untranslatedMessages;
        }
@@ -196,36 +462,17 @@ class languages {
         * @return The duplicate messages for this language.
         */
        public function getDuplicateMessages( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $duplicateMessages = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( @$this->mTranslatableMessages[$key] == $value ) {
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       if ( $this->mGeneralMessages['translatable'][$key] == $value ) {
                                $duplicateMessages[$key] = $value;
                        }
                }
                return $duplicateMessages;
        }
 
-       /**
-        * Get the obsolete messages for a specific language.
-        *
-        * @param $code The langauge code.
-        *
-        * @return The obsolete messages for this language.
-        */
-       public function getObsoleteMessages( $code ) {
-               $this->loadTranslatableMessages();
-               $this->loadMessages( $code );
-               $obsoleteMessages = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( !isset( $this->mTranslatableMessages[$key] ) ) {
-                               $obsoleteMessages[$key] = $value;
-                       }
-               }
-               return $obsoleteMessages;
-       }
-
        /**
         * Get the messages which do not use some variables.
         *
@@ -234,23 +481,21 @@ class languages {
         * @return The messages which do not use some variables in this language.
         */
        public function getMessagesWithoutVariables( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $variables = array( '\$1', '\$2', '\$3', '\$4', '\$5', '\$6', '\$7', '\$8', '\$9' );
                $messagesWithoutVariables = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( isset( $this->mTranslatableMessages[$key] ) ) {
-                               $missing = false;
-                               foreach ( $variables as $var ) {
-                                       if ( preg_match( "/$var/sU", $this->mTranslatableMessages[$key] ) &&
-                                               !preg_match( "/$var/sU", $value ) ) {
-                                               $missing = true;
-                                       }
-                               }
-                               if ( $missing ) {
-                                       $messagesWithoutVariables[$key] = $value;
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       $missing = false;
+                       foreach ( $variables as $var ) {
+                               if ( preg_match( "/$var/sU", $this->mGeneralMessages['translatable'][$key] ) &&
+                                       !preg_match( "/$var/sU", $value ) ) {
+                                       $missing = true;
                                }
                        }
+                       if ( $missing ) {
+                               $messagesWithoutVariables[$key] = $value;
+                       }
                }
                return $messagesWithoutVariables;
        }
@@ -263,12 +508,11 @@ class languages {
         * @return The empty messages for this language.
         */
        public function getEmptyMessages( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $emptyMessages = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( isset( $this->mTranslatableMessages[$key] ) &&
-                               ( $this->mMessages[$code][$key] === '' || $this->mMessages[$code][$key] === '-' ) ) {
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       if ( $value === '' || $value === '-' ) {
                                $emptyMessages[$key] = $value;
                        }
                }
@@ -283,12 +527,11 @@ class languages {
         * @return The messages with trailing whitespace in this language.
         */
        public function getMessagesWithWhitespace( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $messagesWithWhitespace = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( isset( $this->mTranslatableMessages[$key] ) && $this->mTranslatableMessages[$key] !== '' &&
-                               $value !== rtrim( $value ) ) {
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       if ( $this->mGeneralMessages['translatable'][$key] !== '' && $value !== rtrim( $value ) ) {
                                $messagesWithWhitespace[$key] = $value;
                        }
                }
@@ -303,7 +546,7 @@ class languages {
         * @return The non-XHTML messages for this language.
         */
        public function getNonXHTMLMessages( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $wrongPhrases = array(
                        '<hr *\\?>',
@@ -313,8 +556,8 @@ class languages {
                );
                $wrongPhrases = '~(' . implode( '|', $wrongPhrases ) . ')~sDu';
                $nonXHTMLMessages = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( isset( $this->mTranslatableMessages[$key] ) && preg_match( $wrongPhrases, $value ) ) {
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       if ( preg_match( $wrongPhrases, $value ) ) {
                                $nonXHTMLMessages[$key] = $value;
                        }
                }
@@ -329,7 +572,7 @@ class languages {
         * @return The messages which include wrong characters in this language.
         */
        public function getMessagesWithWrongChars( $code ) {
-               $this->loadTranslatableMessages();
+               $this->loadGeneralMessages();
                $this->loadMessages( $code );
                $wrongChars = array(
                        '[LRM]' => "\xE2\x80\x8E",
@@ -346,16 +589,16 @@ class languages {
                        '[FFFD]'=> "\xEF\xBF\xBD",
                );
                $wrongRegExp = '/(' . implode( '|', array_values( $wrongChars ) ) . ')/sDu';
-               $nonXHTMLMessages = array();
-               foreach ( $this->mMessages[$code] as $key => $value ) {
-                       if ( isset( $this->mTranslatableMessages[$key] ) && preg_match( $wrongRegExp, $value ) ) {
+               $wrongCharsMessages = array();
+               foreach ( $this->mMessages[$code]['translated'] as $key => $value ) {
+                       if ( preg_match( $wrongRegExp, $value ) ) {
                                foreach ( $wrongChars as $viewableChar => $hiddenChar ) {
                                        $value = str_replace( $hiddenChar, $viewableChar, $value );
                                }
-                               $nonXHTMLMessages[$key] = $value;
+                               $wrongCharsMessages[$key] = $value;
                        }
                }
-               return $nonXHTMLMessages;
+               return $wrongCharsMessages;
        }
 
        /**
@@ -369,33 +612,34 @@ class languages {
         * @param $wikilang The langauge of the wiki to display the list in, for the links (optional)
         */
        public function outputMessagesList( $messages, $code, $text = '', $level = 2, $links = false, $wikilang = null ) {
-               if ( count( $messages ) > 0 ) {
-                       if ( $text ) {
-                               echo "$text\n";
-                       }
-                       if ( $level == 1 ) {
-                               echo "[messages are hidden]\n";
-                       } else {
-                               foreach ( $messages as $key => $value ) {
-                                       if ( $links ) {
-                                               $displayKey = ucfirst( $key );
-                                               if ( !isset( $wikilang ) ) {
-                                                       global $wgContLang;
-                                                       $wikilang = $wgContLang->getCode();
-                                               }
-                                               if ( $code == $wikilang ) {
-                                                       $displayKey = "[[MediaWiki:$displayKey|$key]]";
-                                               } else {
-                                                       $displayKey = "[[MediaWiki:$displayKey/$code|$key]]";
-                                               }
-                                       } else {
-                                               $displayKey = $key;
+               if ( count( $messages ) == 0 ) {
+                       return;
+               }
+               if ( $text ) {
+                       echo "$text\n";
+               }
+               if ( $level == 1 ) {
+                       echo "[messages are hidden]\n";
+               } else {
+                       foreach ( $messages as $key => $value ) {
+                               if ( $links ) {
+                                       $displayKey = ucfirst( $key );
+                                       if ( !isset( $wikilang ) ) {
+                                               global $wgContLang;
+                                               $wikilang = $wgContLang->getCode();
                                        }
-                                       if ( $level == 2 ) {
-                                               echo "* $displayKey\n";
+                                       if ( $code == $wikilang ) {
+                                               $displayKey = "[[MediaWiki:$displayKey|$key]]";
                                        } else {
-                                               echo "* $displayKey:            '$value'\n";
+                                               $displayKey = "[[MediaWiki:$displayKey/$code|$key]]";
                                        }
+                               } else {
+                                       $displayKey = $key;
+                               }
+                               if ( $level == 2 ) {
+                                       echo "* $displayKey\n";
+                               } else {
+                                       echo "* $displayKey:            '$value'\n";
                                }
                        }
                }
index 7f0a6fd..5f51083 100644 (file)
@@ -148,36 +148,39 @@ $wgOut->element( 'Problematic', true );
 $wgOut->element( '%', true );
 $wgOut->blockend();
 
-foreach ( $wgLanguages->getList() as $code ) {
+$wgGeneralMessages = $wgLanguages->getGeneralMessages();
+$wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
+
+foreach ( $wgLanguages->getLanguages() as $code ) {
        # Don't check English or RTL English
        if ( $code == 'en' || $code == 'enRTL' ) {
                continue;
        }
 
        # 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 );
-       $obsoleteMessagesNumber = count( $wgLanguages->getObsoleteMessages( $code ) );
-       $obsoleteMessagesPercent = $wgOut->formatPercent( $obsoleteMessagesNumber, $translatedMessagesNumber, true );
+       $language = $wgContLang->getLanguageName( $code );
+       $messages = $wgLanguages->getMessages( $code );
+       $messagesNumber = count( $messages['translated'] );
+       $requiredMessagesNumber = count( $messages['required'] );
+       $requiredMessagesPercent = $wgOut->formatPercent( $requiredMessagesNumber, $wgRequiredMessagesNumber );
+       $obsoleteMessagesNumber = count( $messages['obsolete'] );
+       $obsoleteMessagesPercent = $wgOut->formatPercent( $obsoleteMessagesNumber, $messagesNumber, true );
        $messagesWithoutVariables = $wgLanguages->getMessagesWithoutVariables( $code );
        $emptyMessages = $wgLanguages->getEmptyMessages( $code );
        $messagesWithWhitespace = $wgLanguages->getMessagesWithWhitespace( $code );
        $nonXHTMLMessages = $wgLanguages->getNonXHTMLMessages( $code );
        $messagesWithWrongChars = $wgLanguages->getMessagesWithWrongChars( $code );
        $problematicMessagesNumber = count( array_unique( array_merge( $messagesWithoutVariables, $emptyMessages, $messagesWithWhitespace, $nonXHTMLMessages, $messagesWithWrongChars ) ) );
-       $problematicMessagesPercent = $wgOut->formatPercent( $problematicMessagesNumber, $translatedMessagesNumber, true );
+       $problematicMessagesPercent = $wgOut->formatPercent( $problematicMessagesNumber, $messagesNumber, true );
 
        # Output them
        $wgOut->blockstart();
-       $wgOut->element( "$name ($code)" );
-       $wgOut->element( "$translatedMessagesNumber/$translatableMessagesNumber" );
-       $wgOut->element( $translatedMessagesPercent );
-       $wgOut->element( "$obsoleteMessagesNumber/$translatedMessagesNumber" );
+       $wgOut->element( "$language ($code)" );
+       $wgOut->element( "$requiredMessagesNumber/$wgRequiredMessagesNumber" );
+       $wgOut->element( $requiredMessagesPercent );
+       $wgOut->element( "$obsoleteMessagesNumber/$messagesNumber" );
        $wgOut->element( $obsoleteMessagesPercent );
-       $wgOut->element( "$problematicMessagesNumber/$translatedMessagesNumber" );
+       $wgOut->element( "$problematicMessagesNumber/$messagesNumber" );
        $wgOut->element( $problematicMessagesPercent );
        $wgOut->blockend();
 }