# Read language names
global $wgLanguageNames;
-require_once( __DIR__ . '/Names.php' );
+require_once __DIR__ . '/Names.php';
if ( function_exists( 'mb_strtoupper' ) ) {
mb_internal_encoding( 'UTF-8' );
public $mVariants, $mCode, $mLoaded = false;
public $mMagicExtensions = array(), $mMagicHookDone = false;
- private $mHtmlCode = null;
+ private $mHtmlCode = null, $mParentLanguage = false;
public $dateFormatStrings = array();
public $mExtendedSpecialPageAliases;
'seconds' => 1,
);
+ /**
+ * Cache for language fallbacks.
+ * @see Language::getFallbacksIncludingSiteLanguage
+ * @since 1.21
+ * @var array
+ */
+ static private $fallbackLanguageCache = array();
+
/**
* Get a cached or new language object for a given language code
* @param $code String
public static function isKnownLanguageTag( $tag ) {
static $coreLanguageNames;
+ // Quick escape for invalid input to avoid exceptions down the line
+ // when code tries to process tags which are not valid at all.
+ if ( !self::isValidBuiltInCode( $tag ) ) {
+ return false;
+ }
+
if ( $coreLanguageNames === null ) {
- include( MWInit::compiledPath( 'languages/Names.php' ) );
+ include MWInit::compiledPath( 'languages/Names.php' );
}
if ( isset( $coreLanguageNames[$tag] )
}
if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
- include_once( "$IP/languages/classes/$class.php" );
+ include_once "$IP/languages/classes/$class.php";
}
}
static $coreLanguageNames;
if ( $coreLanguageNames === null ) {
- include( MWInit::compiledPath( 'languages/Names.php' ) );
+ include MWInit::compiledPath( 'languages/Names.php' );
}
$names = array();
* @param MWTimestamp $relativeTo Base timestamp
* @param User $user User preferences to use
* @return string Human timestamp
- * @since 1.21
+ * @since 1.22
*/
public function getHumanTimestamp( MWTimestamp $ts, MWTimestamp $relativeTo, User $user ) {
$diff = $ts->diff( $relativeTo );
}
/**
+ * Get all magic words from cache.
* @return array
*/
function getMagicWords() {
return self::$dataCache->getItem( $this->mCode, 'magicWords' );
}
+ /**
+ * Run the LanguageGetMagic hook once.
+ */
protected function doMagicHook() {
if ( $this->mMagicHookDone ) {
return;
* @param $mw
*/
function getMagic( $mw ) {
- $this->doMagicHook();
+ // Saves a function call
+ if ( ! $this->mMagicHookDone ) {
+ $this->doMagicHook();
+ }
if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
$rawEntry = $this->mMagicExtensions[$mw->mId];
} else {
- $magicWords = $this->getMagicWords();
- if ( isset( $magicWords[$mw->mId] ) ) {
- $rawEntry = $magicWords[$mw->mId];
- } else {
- $rawEntry = false;
- }
+ $rawEntry = self::$dataCache->getSubitem(
+ $this->mCode, 'magicWords', $mw->mId );
}
if ( !is_array( $rawEntry ) ) {
* @return string Correct form of plural for $count in this language
*/
function convertPlural( $count, $forms ) {
- if ( !count( $forms ) ) {
- return '';
- }
-
// Handle explicit n=pluralform cases
foreach ( $forms as $index => $form ) {
if ( preg_match( '/\d+=/i', $form ) ) {
unset( $forms[$index] );
}
}
+
$forms = array_values( $forms );
+ if ( !count( $forms ) ) {
+ return '';
+ }
$pluralForm = $this->getPluralRuleIndexNumber( $count );
$pluralForm = min( $pluralForm, count( $forms ) - 1 );
return $this;
}
+ /**
+ * Get the "parent" language which has a converter to convert a "compatible" language
+ * (in another variant) to this language (eg. zh for zh-cn, but not en for en-gb).
+ *
+ * @return Language|null
+ * @since 1.22
+ */
+ public function getParentLanguage() {
+ if ( $this->mParentLanguage !== false ) {
+ return $this->mParentLanguage;
+ }
+
+ $pieces = explode( '-', $this->getCode() );
+ $code = $pieces[0];
+ if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
+ $this->mParentLanguage = null;
+ return null;
+ }
+ $lang = Language::factory( $code );
+ if ( !$lang->hasVariant( $this->getCode() ) ) {
+ $this->mParentLanguage = null;
+ return null;
+ }
+
+ $this->mParentLanguage = $lang;
+ return $lang;
+ }
+
/**
* Get the RFC 3066 code for this language object
*
*/
public function setCode( $code ) {
$this->mCode = $code;
- // Ensure we don't leave an incorrect html code lying around
+ // Ensure we don't leave incorrect cached data lying around
$this->mHtmlCode = null;
+ $this->mParentLanguage = false;
}
/**
}
}
+ /**
+ * Get the ordered list of fallback languages, ending with the fallback
+ * language chain for the site language.
+ *
+ * @since 1.22
+ * @param string $code Language code
+ * @return array array( fallbacks, site fallbacks )
+ */
+ public static function getFallbacksIncludingSiteLanguage( $code ) {
+ global $wgLanguageCode;
+
+ // Usually, we will only store a tiny number of fallback chains, so we
+ // keep them in static memory.
+ $cacheKey = "{$code}-{$wgLanguageCode}";
+
+ if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
+ $fallbacks = self::getFallbacksFor( $code );
+
+ // Append the site's fallback chain, including the site language itself
+ $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
+ array_unshift( $siteFallbacks, $wgLanguageCode );
+
+ // Eliminate any languages already included in the chain
+ $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
+
+ self::$fallbackLanguageCache[$cacheKey] = array( $fallbacks, $siteFallbacks );
+ }
+ return self::$fallbackLanguageCache[$cacheKey];
+ }
+
/**
* Get all messages for a given language
* WARNING: this may take a long time. If you just need all message *keys*
/**
* Get the plural rule types for the language
- * @since 1.21
+ * @since 1.22
* @return array Associative array with plural form number and plural rule type as key-value pairs
*/
public function getPluralRuleTypes() {
* Find the plural rule type appropriate for the given number
* For example, if the language is set to Arabic, getPluralType(5) should
* return 'few'.
- * @since 1.21
+ * @since 1.22
* @return string The name of the plural rule type, e.g. one, two, few, many
*/
public function getPluralRuleType( $number ) {