mLang = $langobj; } function autoConvertToAllVariants( $text ) { return array( $this->mLang->getCode() => $text ); } function convert( $t ) { return $t; } function convertTo( $text, $variant ) { return $text; } function convertTitle( $t ) { return $t->getPrefixedText(); } function getVariants() { return array( $this->mLang->getCode() ); } function getPreferredVariant() { return $this->mLang->getCode(); } function getDefaultVariant() { return $this->mLang->getCode(); } function getURLVariant() { return ''; } function getConvRuleTitle() { return false; } function findVariantLink( &$l, &$n, $ignoreOtherCond = false ) { } function getExtraHashOptions() { return ''; } function getParsedTitle() { return ''; } function markNoConversion( $text, $noParse = false ) { return $text; } function convertCategoryKey( $key ) { return $key; } function convertLinkToAllVariants( $text ) { return $this->autoConvertToAllVariants( $text ); } function armourMath( $text ) { return $text; } } /** * Internationalisation code * @ingroup Language */ class Language { /** * @var LanguageConverter */ var $mConverter; var $mVariants, $mCode, $mLoaded = false; var $mMagicExtensions = array(), $mMagicHookDone = false; private $mHtmlCode = null; var $dateFormatStrings = array(); var $mExtendedSpecialPageAliases; protected $namespaceNames, $mNamespaceIds, $namespaceAliases; /** * ReplacementArray object caches */ var $transformData = array(); /** * @var LocalisationCache */ static public $dataCache; static public $mLangObjCache = array(); static public $mWeekdayMsgs = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' ); static public $mWeekdayAbbrevMsgs = array( 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ); static public $mMonthMsgs = array( 'january', 'february', 'march', 'april', 'may_long', 'june', 'july', 'august', 'september', 'october', 'november', 'december' ); static public $mMonthGenMsgs = array( 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen', 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ); static public $mMonthAbbrevMsgs = array( 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ); static public $mIranianCalendarMonthMsgs = array( 'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3', 'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6', 'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9', 'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12' ); static public $mHebrewCalendarMonthMsgs = array( 'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3', 'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6', 'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9', 'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12', 'hebrew-calendar-m6a', 'hebrew-calendar-m6b' ); static public $mHebrewCalendarMonthGenMsgs = array( 'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen', 'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen', 'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen', 'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen', 'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen' ); static public $mHijriCalendarMonthMsgs = array( 'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3', 'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6', 'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9', 'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12' ); /** * @since 1.20 * @var array */ static public $durationIntervals = array( 'millennia' => 31557600000, 'centuries' => 3155760000, 'decades' => 315576000, 'years' => 31557600, // 86400 * 365.25 'weeks' => 604800, 'days' => 86400, 'hours' => 3600, 'minutes' => 60, 'seconds' => 1, ); /** * Get a cached language object for a given language code * @param $code String * @return Language */ static function factory( $code ) { if ( !isset( self::$mLangObjCache[$code] ) ) { if ( count( self::$mLangObjCache ) > 10 ) { // Don't keep a billion objects around, that's stupid. self::$mLangObjCache = array(); } self::$mLangObjCache[$code] = self::newFromCode( $code ); } return self::$mLangObjCache[$code]; } /** * Create a language object for a given language code * @param $code String * @throws MWException * @return Language */ protected static function newFromCode( $code ) { // Protect against path traversal below if ( !Language::isValidCode( $code ) || strcspn( $code, ":/\\\000" ) !== strlen( $code ) ) { throw new MWException( "Invalid language code \"$code\"" ); } if ( !Language::isValidBuiltInCode( $code ) ) { // It's not possible to customise this code with class files, so // just return a Language object. This is to support uselang= hacks. $lang = new Language; $lang->setCode( $code ); return $lang; } // 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; } // 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'" ); } $class = self::classFromCode( $fallbackCode ); self::preloadLanguageClass( $class ); if ( MWInit::classExists( $class ) ) { $lang = Language::newFromCode( $fallbackCode ); $lang->setCode( $code ); return $lang; } } throw new MWException( "Invalid fallback sequence for language '$code'" ); } /** * Returns true if a language code string is of a valid form, whether or * not it exists. This includes codes which are used solely for * customisation via the MediaWiki namespace. * * @param $code string * * @return bool */ public static function isValidCode( $code ) { return // People think language codes are html safe, so enforce it. // Ideally we should only allow a-zA-Z0-9- // but, .+ and other chars are often used for {{int:}} hacks // see bugs 37564, 37587, 36938 strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code ) && !preg_match( Title::getTitleInvalidRegex(), $code ); } /** * Returns true if a language code is of a valid form for the purposes of * internal customisation of MediaWiki, via Messages*.php. * * @param $code string * * @throws MWException * @since 1.18 * @return bool */ public static function isValidBuiltInCode( $code ) { if ( !is_string( $code ) ) { $type = gettype( $code ); if ( $type === 'object' ) { $addmsg = " of class " . get_class( $code ); } else { $addmsg = ''; } throw new MWException( __METHOD__ . " must be passed a string, $type given$addmsg" ); } return preg_match( '/^[a-z0-9-]+$/i', $code ); } /** * @param $code * @return String Name of the language class */ public static function classFromCode( $code ) { if ( $code == 'en' ) { return 'Language'; } else { return 'Language' . str_replace( '-', '_', ucfirst( $code ) ); } } /** * Includes language class files * * @param $class string Name of the language class */ public static function preloadLanguageClass( $class ) { global $IP; if ( $class === 'Language' ) { return; } if ( !defined( 'MW_COMPILED' ) ) { if ( file_exists( "$IP/languages/classes/$class.php" ) ) { include_once( "$IP/languages/classes/$class.php" ); } } } /** * Get the LocalisationCache instance * * @return LocalisationCache */ public static function getLocalisationCache() { if ( is_null( self::$dataCache ) ) { global $wgLocalisationCacheConf; $class = $wgLocalisationCacheConf['class']; self::$dataCache = new $class( $wgLocalisationCacheConf ); } return self::$dataCache; } function __construct() { $this->mConverter = new FakeConverter( $this ); // Set the code to the name of the descendant if ( get_class( $this ) == 'Language' ) { $this->mCode = 'en'; } else { $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) ); } self::getLocalisationCache(); } /** * Reduce memory usage */ function __destruct() { foreach ( $this as $name => $value ) { unset( $this->$name ); } } /** * Hook which will be called if this is the content language. * Descendants can use this to register hook functions or modify globals */ function initContLang() { } /** * Same as getFallbacksFor for current language. * @return array|bool * @deprecated in 1.19 */ function getFallbackLanguageCode() { wfDeprecated( __METHOD__ ); return self::getFallbackFor( $this->mCode ); } /** * @return array * @since 1.19 */ function getFallbackLanguages() { return self::getFallbacksFor( $this->mCode ); } /** * Exports $wgBookstoreListEn * @return array */ function getBookstoreList() { return self::$dataCache->getItem( $this->mCode, 'bookstoreList' ); } /** * @return array */ public function getNamespaces() { if ( is_null( $this->namespaceNames ) ) { global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces; $this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' ); $validNamespaces = MWNamespace::getCanonicalNamespaces(); $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces; $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace; if ( $wgMetaNamespaceTalk ) { $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk; } else { $talk = $this->namespaceNames[NS_PROJECT_TALK]; $this->namespaceNames[NS_PROJECT_TALK] = $this->fixVariableInNamespace( $talk ); } # Sometimes a language will be localised but not actually exist on this wiki. foreach ( $this->namespaceNames as $key => $text ) { if ( !isset( $validNamespaces[$key] ) ) { unset( $this->namespaceNames[$key] ); } } # The above mixing may leave namespaces out of canonical order. # Re-order by namespace ID number... ksort( $this->namespaceNames ); wfRunHooks( 'LanguageGetNamespaces', array( &$this->namespaceNames ) ); } return $this->namespaceNames; } /** * Arbitrarily set all of the namespace names at once. Mainly used for testing * @param $namespaces Array of namespaces (id => name) */ public function setNamespaces( array $namespaces ) { $this->namespaceNames = $namespaces; } /** * A convenience function that returns the same thing as * getNamespaces() except with the array values changed to ' ' * where it found '_', useful for producing output to be displayed * e.g. in