Attempt to fix Bug 30216 - Improve language fallback loop detection.
authorNiklas Laxström <nikerabbit@users.mediawiki.org>
Thu, 18 Aug 2011 16:41:07 +0000 (16:41 +0000)
committerNiklas Laxström <nikerabbit@users.mediawiki.org>
Thu, 18 Aug 2011 16:41:07 +0000 (16:41 +0000)
Made fallbacks a list per language, instead of scanning them recursively through the languages.

20 files changed:
includes/api/ApiQuerySiteinfo.php
languages/Language.php
languages/messages/MessagesAls.php
languages/messages/MessagesBat_smg.php
languages/messages/MessagesFiu_vro.php
languages/messages/MessagesGan.php
languages/messages/MessagesGan_hant.php
languages/messages/MessagesIi.php
languages/messages/MessagesKaa.php
languages/messages/MessagesKk_cn.php
languages/messages/MessagesKk_tr.php
languages/messages/MessagesMap_bms.php
languages/messages/MessagesQug.php
languages/messages/MessagesRue.php
languages/messages/MessagesRuq.php
languages/messages/MessagesTt.php
languages/messages/MessagesZh_hk.php
languages/messages/MessagesZh_mo.php
languages/messages/MessagesZh_my.php
languages/messages/MessagesZh_tw.php

index 6859604..3c61824 100644 (file)
@@ -140,13 +140,11 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data['rights'] = $GLOBALS['wgRightsText'];
                $data['lang'] = $GLOBALS['wgLanguageCode'];
 
-               $fallbackLang = $wgContLang->getFallbackLanguageCode();
-               $fallbackLangArray = array();
-               while( $fallbackLang ) {
-                       $fallbackLangArray[] = array( 'code' => $fallbackLang );
-                       $fallbackLang = Language::getFallbackFor( $fallbackLang );
+               $fallbacks = array();
+               foreach( $wgContLang->getFallbackLanguages() as $code ) {
+                       $fallbacks[] = array( 'code' => $code );
                }
-               $data['fallback'] = $fallbackLangArray;
+               $data['fallback'] = $fallbacks;
                $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
 
                if ( $wgContLang->isRTL() ) {
index 46de0cd..474fde0 100644 (file)
@@ -148,9 +148,6 @@ class Language {
         * @return Language
         */
        protected static function newFromCode( $code ) {
-               global $IP;
-               static $recursionLevel = 0;
-
                // Protect against path traversal below
                if ( !Language::isValidCode( $code )
                        || strcspn( $code, ":/\\\000" ) !== strlen( $code ) )
@@ -166,35 +163,31 @@ class Language {
                        return $lang;
                }
 
-               if ( $code == 'en' ) {
-                       $class = 'Language';
-               } else {
-                       $class = 'Language' . str_replace( '-', '_', ucfirst( $code ) );
-                       if ( !defined( 'MW_COMPILED' ) ) {
-                               // Preload base classes to work around APC/PHP5 bug
-                               if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
-                                       include_once( "$IP/languages/classes/$class.deps.php" );
-                               }
-                               if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
-                                       include_once( "$IP/languages/classes/$class.php" );
-                               }
-                       }
+               // Check if there is a language class for the code
+               $class = self::classFromCode( $code );
+               self::preloadLanguageClass( $class );
+               if ( MWInit::classExists( $class ) ) {
+                       $lang = new $class;
+                       return $lang;
                }
 
-               if ( $recursionLevel > 5 ) {
-                       throw new MWException( "Language fallback loop detected when creating class $class\n" );
-               }
+               // Keep trying the fallback list until we find an existing class
+               $fallbacks = Language::getFallbacksFor( $code );
+               foreach ( $fallbacks as $fallbackCode ) {
+                       if ( !Language::isValidBuiltInCode( $fallbackCode ) ) {
+                               throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
+                       }
 
-               if ( !MWInit::classExists( $class ) ) {
-                       $fallback = Language::getFallbackFor( $code );
-                       ++$recursionLevel;
-                       $lang = Language::newFromCode( $fallback );
-                       --$recursionLevel;
-                       $lang->setCode( $code );
-               } else {
-                       $lang = new $class;
+                       $class = self::classFromCode( $fallbackCode );
+                       self::preloadLanguageClass( $class );
+                       if ( MWInit::classExists( $class ) ) {
+                               $lang = Language::newFromCode( $fallbackCode );
+                               $lang->setCode( $code );
+                               return $lang;
+                       }
                }
-               return $lang;
+
+               throw new MWException( "Invalid fallback sequence for language '$code'" );
        }
 
        /**
@@ -225,6 +218,32 @@ class Language {
                return preg_match( '/^[a-z0-9-]*$/i', $code );
        }
 
+       public static function classFromCode( $code ) {
+               if ( $code == 'en' ) {
+                       return 'Language';
+               } else {
+                       return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
+               }
+       }
+
+       public static function preloadLanguageClass( $class ) {
+               global $IP;
+
+               if ( $class === 'Language' ) {
+                       return;
+               }
+
+               if ( !defined( 'MW_COMPILED' ) ) {
+                       // Preload base classes to work around APC/PHP5 bug
+                       if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
+                               include_once( "$IP/languages/classes/$class.deps.php" );
+                       }
+                       if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
+                               include_once( "$IP/languages/classes/$class.php" );
+                       }
+               }
+       }
+
        /**
         * Get the LocalisationCache instance
         *
@@ -266,14 +285,21 @@ class Language {
        function initContLang() { }
 
        /**
+        * Same as getFallbacksFor for current language.
         * @return array|bool
+        * @deprecated in 1.19
         */
        function getFallbackLanguageCode() {
-               if ( $this->mCode === 'en' ) {
-                       return false;
-               } else {
-                       return self::$dataCache->getItem( $this->mCode, 'fallback' );
-               }
+               wfDeprecated( __METHOD__ );
+               return self::getFallbackFor( $this->mCode );
+       }
+
+       /**
+        * @return array
+        * @since 1.19
+        */
+       function getFallbackLanguages() {
+               return self::getFallbacksFor( $this->mCode );
        }
 
        /**
@@ -2437,15 +2463,7 @@ class Language {
         * @param $newWords array
         */
        function addMagicWordsByLang( $newWords ) {
-               $code = $this->getCode();
-               $fallbackChain = array();
-               while ( $code && !in_array( $code, $fallbackChain ) ) {
-                       $fallbackChain[] = $code;
-                       $code = self::getFallbackFor( $code );
-               }
-               if ( !in_array( 'en', $fallbackChain ) ) {
-                       $fallbackChain[] = 'en';
-               }
+               $fallbackChain = $this->getFallbackLanguages();
                $fallbackChain = array_reverse( $fallbackChain );
                foreach ( $fallbackChain as $code ) {
                        if ( isset( $newWords[$code] ) ) {
@@ -3294,7 +3312,7 @@ class Language {
        }
 
        /**
-        * Get the fallback for a given language
+        * Get the first fallback for a given language
         *
         * @param $code string
         *
@@ -3302,10 +3320,31 @@ class Language {
         */
        static function getFallbackFor( $code ) {
                if ( $code === 'en' ) {
-                       // Shortcut
                        return false;
                } else {
-                       return self::getLocalisationCache()->getItem( $code, 'fallback' );
+                       $fallbacks = self::getFallbacksFor( $code );
+                       $first = array_shift( $fallbacks );
+                       return $first;
+               }
+       }
+
+       /**
+        * Get the ordered list of fallback languages.
+        *
+        * @since 1.19
+        * @param $code string Language code
+        * @return array
+        */
+       static function getFallbacksFor( $code ) {
+               if ( $code === 'en' ) {
+                       return array();
+               } else {
+                       $v = self::getLocalisationCache()->getItem( $code, 'fallback' );
+                       $v = array_map( 'trim', explode( ',', $v ) );
+                       if ( $v[count( $v ) - 1] !== 'en' ) {
+                               $v[] = 'en';
+                       }
+                       return $v;
                }
        }
 
index 604f49f..867d2a8 100644 (file)
@@ -9,4 +9,4 @@
  * @comment Deprecated code. Falls back to 'gsw'.
  */
 
-$fallback = 'gsw';
+$fallback = 'gsw, de';
index d32e58a..44041f0 100644 (file)
@@ -12,4 +12,4 @@
  *
  */
 
-$fallback = 'sgs';
+$fallback = 'sgs, lt';
index 85c3fd7..9407647 100644 (file)
@@ -9,4 +9,4 @@
  * @comment Deprecated language code. Falls back to 'vro'.
  */
 
-$fallback = 'vro';
+$fallback = 'vro, et';
index 4111735..3850261 100644 (file)
@@ -12,7 +12,7 @@
  * @author Vipuser
  */
 
-$fallback = 'gan-hant';
+$fallback = 'gan-hant, zh-hant, zh-hans';
 
 $namespaceNames = array(
        NS_MEDIA            => 'Media',
@@ -38,5 +38,4 @@ $messages = array(
 'variantname-gan-hans' => '简体',
 'variantname-gan-hant' => '繁體',
 'variantname-gan'      => '贛語原文',
-
 );
index edd1fdb..39acc2c 100644 (file)
@@ -14,7 +14,7 @@
  * @author Vipuser
  */
 
-$fallback = 'zh-hant';
+$fallback = 'zh-hant, zh-hans';
 
 $namespaceNames = array(
        NS_TALK             => '談詑',
index 1dc08fe..a827db8 100644 (file)
  * @author Biŋhai
  */
 
-$fallback = 'zh-cn';
+$fallback = 'zh-cn, zh-hans';
 
 $messages = array(
-# All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).
 'mainpage' => 'ꀨꏾꌠ',
-
 );
index 7b812e2..b645de6 100644 (file)
@@ -14,7 +14,7 @@
  * @author Urhixidur
  */
 
-$fallback = 'kk-latn';
+$fallback = 'kk-latn, kk-cyrl';
 
 $separatorTransformTable = array(
        ',' => "\xc2\xa0",
index e96d890..fba4e9f 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
-$fallback = 'kk-arab';
+$fallback = 'kk-arab, kk-cyrl';
index 41d530a..05627f9 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
-$fallback = 'kk-latn';
+$fallback = 'kk-latn, kk-cyrl';
index 4276ae7..92befb8 100644 (file)
@@ -10,7 +10,7 @@
  * @author לערי ריינהארט
  */
 
-$fallback = 'jv';
+$fallback = 'jv, id';
 
 $messages = array(
 'article'    => 'Isi tulisan',
index 6f419da..2032bf4 100644 (file)
@@ -11,7 +11,7 @@
  * @author Sylvain2803
  */
 
-$fallback = 'qu';
+$fallback = 'qu, es';
 
 $messages = array(
 # Dates
index d0d6cff..2545cb0 100644 (file)
@@ -13,7 +13,7 @@
  * @author Tkalyn
  */
 
-$fallback = 'uk';
+$fallback = 'uk, ru';
 
 $namespaceNames = array(
        NS_MEDIA            => 'Медіа',
index d3d5283..ce2d0ce 100644 (file)
@@ -10,4 +10,4 @@
  *
  */
 
-$fallback = 'ruq-latn';
+$fallback = 'ruq-latn, ro';
index 6cfb678..1cc1166 100644 (file)
@@ -10,4 +10,4 @@
  * @comment Placeholder for Tatar. Falls back to Tatar in Latin script.
  */
 
-$fallback = 'tt-cyrl';
+$fallback = 'tt-cyrl, ru';
index dc88925..70a3612 100644 (file)
@@ -17,7 +17,7 @@
  * @author Yuyu
  */
 
-$fallback = 'zh-hant';
+$fallback = 'zh-hant, zh-hans';
 
 $fallback8bitEncoding = 'Big5-HKSCS';
 
index 6772ef0..37be152 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
-$fallback = 'zh-hk';
+$fallback = 'zh-hk, zh-hant, zh-hans';
index a27ed45..01b5524 100644 (file)
@@ -10,4 +10,4 @@
  */
 
 # Inherit everything for now
-$fallback = 'zh-sg';
+$fallback = 'zh-sg, zh-hans';
index 5ecd229..55f6777 100644 (file)
@@ -22,6 +22,8 @@
  * @author לערי ריינהארט
  */
 
+$fallback = 'zh-hant, zh-hans';
+
 $specialPageAliases = array(
        'Ancientpages'              => array( '最舊頁面' ),
        'Block'                     => array( '查封用戶' ),
@@ -43,8 +45,6 @@ $specialPageAliases = array(
        'Withoutinterwiki'          => array( '沒有跨語言鏈接的頁面' ),
 );
 
-$fallback = 'zh-hant';
-
 $namespaceNames = array(
        NS_USER             => '使用者',
        NS_USER_TALK        => '使用者討論',