function parserConvert($t, $p) {return $t;}
function getVariants() { return array( $this->mLang->getCode() ); }
function getPreferredVariant() {return $this->mLang->getCode(); }
- function findVariantLink(&$l, &$n) {}
+ function findVariantLink(&$l, &$n, $forTemplate = false) {}
function getExtraHashOptions() {return '';}
function getParsedTitle() {return '';}
function markNoConversion($text, $noParse=false) {return $text;}
var $mMagicExtensions = array(), $mMagicHookDone = false;
static public $mLocalisationKeys = array( 'fallback', 'namespaceNames',
- 'skinNames', 'mathNames',
- 'bookstoreList', 'magicWords', 'messages', 'rtl', 'digitTransformTable',
+ 'skinNames', 'mathNames',
+ 'bookstoreList', 'magicWords', 'messages', 'rtl', 'digitTransformTable',
'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
- 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
- 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
- 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases' );
+ 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
+ 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
+ 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
+ 'imageFiles'
+ );
- static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
- 'dateFormats', 'defaultUserOptionOverrides', 'magicWords' );
+ static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
+ 'dateFormats', 'defaultUserOptionOverrides', 'magicWords', 'imageFiles' );
static public $mMergeableListKeys = array( 'extraUserToggles' );
static public $mMergeableAliasListKeys = array( 'specialPageAliases' );
static public $mLocalisationCache = array();
+ static public $mLangObjCache = array();
static public $mWeekdayMsgs = array(
'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
);
/**
- * Create a language object for a given language code
+ * Get a cached language object for a given language code
*/
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
+ */
+ protected static function newFromCode( $code ) {
global $IP;
static $recursionLevel = 0;
-
if ( $code == 'en' ) {
$class = 'Language';
} else {
if( ! class_exists( $class ) ) {
$fallback = Language::getFallbackFor( $code );
++$recursionLevel;
- $lang = Language::factory( $fallback );
+ $lang = Language::newFromCode( $fallback );
--$recursionLevel;
$lang->setCode( $code );
} else {
$lang = new $class;
}
-
return $lang;
}
}
}
+ /**
+ * 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
return $this->datePreferenceMigrationMap;
}
+ function getImageFile( $image ) {
+ $this->load();
+ return $this->imageFiles[$image];
+ }
+
function getDefaultUserOptionOverrides() {
$this->load();
# XXX - apparently some languageas get empty arrays, didn't get to it yet -- midom
}
/**
- * Ugly hack to get a message maybe from the MediaWiki namespace, if this
- * language object is the content or user language.
+ * Get a message from the MediaWiki namespace.
+ *
+ * @param $msg String: message name
+ * @return string
*/
function getMessageFromDB( $msg ) {
- global $wgContLang, $wgLang;
- if ( $wgContLang->getCode() == $this->getCode() ) {
- # Content language
- return wfMsgForContent( $msg );
- } elseif ( $wgLang->getCode() == $this->getCode() ) {
- # User language
- return wfMsg( $msg );
- } else {
- # Neither, get from localisation
- return $this->getMessage( $msg );
- }
+ return wfMsgExt( $msg, array( 'parsemag', 'language' => $this ) );
}
function getLanguageName( $code ) {
* (abu-mami@kaluach.net, http://www.kaluach.net), who permitted
* to translate the relevant functions into PHP and release them under
* GNU GPL.
+ *
+ * The months are counted from Tishrei = 1. In a leap year, Adar I is 13
+ * and Adar II is 14. In a non-leap year, Adar is 6.
*/
private static function tsToHebrew( $ts ) {
# Parse date
return $string;
}
- # MySQL fulltext index doesn't grok utf-8, so we
- # need to fold cases and convert to hex
wfProfileIn( __METHOD__ );
- if( function_exists( 'mb_strtolower' ) ) {
- $out = preg_replace(
- "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
- "'U8' . bin2hex( \"$1\" )",
- mb_strtolower( $string ) );
- } else {
- list( , $wikiLowerChars ) = self::getCaseMaps();
+
+ // MySQL fulltext index doesn't grok utf-8, so we
+ // need to fold cases and convert to hex
+ $out = preg_replace_callback(
+ "/([\\xc0-\\xff][\\x80-\\xbf]*)/",
+ array( $this, 'stripForSearchCallback' ),
+ $this->lc( $string ) );
+
+ // And to add insult to injury, the default indexing
+ // ignores short words... Pad them so we can pass them
+ // through without reconfiguring the server...
+ $minLength = $this->minSearchLength();
+ if( $minLength > 1 ) {
+ $n = $minLength-1;
$out = preg_replace(
- "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
- "'U8' . bin2hex( strtr( \"\$1\", \$wikiLowerChars ) )",
- $string );
+ "/\b(\w{1,$n})\b/",
+ "$1U800",
+ $out );
}
+
+ // Periods within things like hostnames and IP addresses
+ // are also important -- we want a search for "example.com"
+ // or "192.168.1.1" to work sanely.
+ //
+ // MySQL's search seems to ignore them, so you'd match on
+ // "example.wikipedia.com" and "192.168.83.1" as well.
+ $out = preg_replace(
+ "/(\w)\.(\w|\*)/u",
+ "$1U82e$2",
+ $out );
+
wfProfileOut( __METHOD__ );
return $out;
}
+
+ /**
+ * Armor a case-folded UTF-8 string to get through MySQL's
+ * fulltext search without being mucked up by funny charset
+ * settings or anything else of the sort.
+ */
+ protected function stripForSearchCallback( $matches ) {
+ return 'U8' . bin2hex( $matches[1] );
+ }
+
+ /**
+ * Check MySQL server's ft_min_word_len setting so we know
+ * if we need to pad short words...
+ */
+ protected function minSearchLength() {
+ if( !isset( $this->minSearchLength ) ) {
+ $sql = "show global variables like 'ft\\_min\\_word\\_len'";
+ $dbr = wfGetDB( DB_SLAVE );
+ $result = $dbr->query( $sql );
+ $row = $result->fetchObject();
+ $result->free();
+
+ if( $row && $row->Variable_name == 'ft_min_word_len' ) {
+ $this->minSearchLength = intval( $row->Value );
+ } else {
+ $this->minSearchLength = 0;
+ }
+ }
+ return $this->minSearchLength;
+ }
function convertForSearchResult( $termsArray ) {
# some languages, e.g. Chinese, need to do a conversion
} else {
# Fall back to English if local list is incomplete
$magicWords =& Language::getMagicWords();
- if ( !isset($magicWords[$mw->mId]) ) { throw new MWException("Magic word not found" ); }
+ if ( !isset($magicWords[$mw->mId]) ) {
+ throw new MWException("Magic word '{$mw->mId}' not found" );
+ }
$rawEntry = $magicWords[$mw->mId];
}
}
// Fail fast
if ( !file_exists($file) )
- throw new MWException( 'Aliases file does not exist' );
+ throw new MWException( "Aliases file does not exist: $file" );
$aliases = array();
require($file);
// Check the availability of aliases
if ( !isset($aliases['en']) )
- throw new MWException( 'Malformed aliases file' );
+ throw new MWException( "Malformed aliases file: $file" );
+ // Merge all aliases in fallback chain
$code = $this->getCode();
+ do {
+ if ( !isset($aliases[$code]) ) continue;
- if ( isset($aliases[$code]) ) {
$aliases[$code] = $this->fixSpecialPageAliases( $aliases[$code] );
/* Merge the aliases, THIS will break if there is special page name
* which looks like a numerical key, thanks to PHP...
- * See the comments for wfArrayMerge in GlobalSettings.php. */
+ * See the array_merge_recursive manual entry */
$this->mExtendedSpecialPageAliases = array_merge_recursive(
$this->mExtendedSpecialPageAliases, $aliases[$code] );
- }
- /* Add the English aliases to the end of list as aliases... unless we
- * already added them! */
- if ( $code !== 'en' ) {
- $aliases['en'] = $this->fixSpecialPageAliases( $aliases['en'] );
- $this->mExtendedSpecialPageAliases = array_merge_recursive(
- $this->mExtendedSpecialPageAliases, $aliases['en'] );
- }
-
+ } while ( $code = self::getFallbackFor( $code ) );
}
wfRunHooks( 'LanguageGetSpecialPageAliases',
- array( &$this->mExtendedSpecialPageAliases, $code ) );
+ array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
}
return $this->mExtendedSpecialPageAliases;
* </code>
*
* See LanguageGu.php for the Gujarati implementation and
- * LanguageIs.php for the , => . and . => , implementation.
+ * $separatorTransformTable on MessageIs.php for
+ * the , => . and . => , implementation.
*
* @todo check if it's viable to use localeconv() for the decimal
- * seperator thing.
+ * separator thing.
* @param $number Mixed: the string to be formatted, should be an integer
* or a floating point number.
* @param $nocommafy Bool: set to true for special numbers like dates
if ($i == $m) {
$s = $l[$i];
} else if ($i == $m - 1) {
- $s = $l[$i] . ' ' . $this->getMessageFromDB( 'and' ) . ' ' . $s;
+ $s = $l[$i] . $this->getMessageFromDB( 'and' ) . $this->getMessageFromDB( 'word-separator' ) . $s;
} else {
- $s = $l[$i] . ', ' . $s;
+ $s = $l[$i] . $this->getMessageFromDB( 'comma-separator' ) . $s;
}
}
return $s;
}
+
+ /**
+ * Take a list of strings and build a locale-friendly comma-separated
+ * list, using the local comma-separator message.
+ * @param $list array of strings to put in a comma list
+ * @return string
+ */
+ function commaList( $list, $forContent = false ) {
+ return implode(
+ $list,
+ wfMsgExt( 'comma-separator', array( 'escapenoentities', 'language' => $this ) ) );
+ }
+
+ /**
+ * Same as commaList, but separate it with the pipe instead.
+ * @param $list array of strings to put in a pipe list
+ * @return string
+ */
+ function pipeList( $list ) {
+ return implode(
+ $list,
+ wfMsgExt( 'pipe-separator', array( 'escapenoentities', 'language' => $this ) ) );
+ }
/**
* Truncate a string to a specified length in bytes, appending an optional
if ( !count($forms) ) { return ''; }
$forms = $this->preConvertPlural( $forms, 2 );
- return ( abs($count) == 1 ) ? $forms[0] : $forms[1];
+ return ( $count == 1 ) ? $forms[0] : $forms[1];
}
/**
* @param $nt Mixed: the title object of the link
* @return null the input parameters may be modified upon return
*/
- function findVariantLink( &$link, &$nt ) {
- $this->mConverter->findVariantLink($link, $nt);
+ function findVariantLink( &$link, &$nt, $forTemplate = false ) {
+ $this->mConverter->findVariantLink($link, $nt, $forTemplate );
}
/**
*/
static function loadLocalisation( $code, $disableCache = false ) {
static $recursionGuard = array();
- global $wgMemc, $wgCheckSerialized;
+ global $wgMemc, $wgEnableSerializedMessages, $wgCheckSerialized;
if ( !$code ) {
throw new MWException( "Invalid language code requested" );
wfProfileIn( __METHOD__ );
# Try the serialized directory
- $cache = wfGetPrecompiledData( self::getFileName( "Messages", $code, '.ser' ) );
- if ( $cache ) {
- if ( $wgCheckSerialized && self::isLocalisationOutOfDate( $cache ) ) {
- $cache = false;
- wfDebug( "Language::loadLocalisation(): precompiled data file for $code is out of date\n" );
- } else {
- self::$mLocalisationCache[$code] = $cache;
- wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
- wfProfileOut( __METHOD__ );
- return self::$mLocalisationCache[$code]['deps'];
+ if( $wgEnableSerializedMessages ) {
+ $cache = wfGetPrecompiledData( self::getFileName( "Messages", $code, '.ser' ) );
+ if ( $cache ) {
+ if ( $wgCheckSerialized && self::isLocalisationOutOfDate( $cache ) ) {
+ $cache = false;
+ wfDebug( "Language::loadLocalisation(): precompiled data file for $code is out of date\n" );
+ } else {
+ self::$mLocalisationCache[$code] = $cache;
+ wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
+ wfProfileOut( __METHOD__ );
+ return self::$mLocalisationCache[$code]['deps'];
+ }
}
}
# Replace spaces with underscores in namespace names
$cache['namespaceNames'] = str_replace( ' ', '_', $cache['namespaceNames'] );
- # And do the same for specialpage aliases.
- $cache['specialPageAliases'] =
- $this->fixSpecialPageAliases( $cache['specialPageAliases'] );
+ # And do the same for specialpage aliases. $page is an array.
+ foreach ( $cache['specialPageAliases'] as &$page ) {
+ $page = str_replace( ' ', '_', $page );
+ }
+ # Decouple the reference to prevent accidental damage
+ unset($page);
# Save to both caches
self::$mLocalisationCache[$code] = $cache;