'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
'imageFiles', 'preloadedMessages', 'namespaceGenderAliases',
- 'digitGroupingPattern', 'pluralRules'
+ 'digitGroupingPattern', 'pluralRules', 'compiledPluralRules',
);
/**
* by a fallback sequence.
*/
static public $mergeableMapKeys = array( 'messages', 'namespaceNames',
- 'dateFormats', 'imageFiles', 'preloadedMessages', 'pluralRules'
+ 'dateFormats', 'imageFiles', 'preloadedMessages'
);
/**
*/
static public $preloadedKeys = array( 'dateFormats', 'namespaceNames' );
- /*
- * Associative array containing plural rules.
+ /**
+ * Associative array of cached plural rules. The key is the language code,
+ * the value is an array of plural rules for that language.
*/
- var $pluralRules = array();
+ var $pluralRules = null;
var $mergeableKeys = null;
* for $wgLocalisationCacheConf.
*
* @param $conf Array
+ * @throws MWException
*/
function __construct( $conf ) {
global $wgCacheDirectory;
$this->$var = $conf[$var];
}
}
- $this->readPluralRules();
}
/**
}
$deps = $this->store->get( $code, 'deps' );
- $keys = $this->store->get( $code, 'list', 'messages' );
+ $keys = $this->store->get( $code, 'list' );
$preload = $this->store->get( $code, 'preload' );
// Different keys may expire separately, at least in LCStore_Accel
if ( $deps === null || $keys === null || $preload === null ) {
/**
* Initialise a language in this object. Rebuild the cache if necessary.
* @param $code
+ * @throws MWException
*/
protected function initLanguage( $code ) {
if ( isset( $this->initialisedLangs[$code] ) ) {
* Read a PHP file containing localisation data.
* @param $_fileName
* @param $_fileType
+ * @throws MWException
* @return array
*/
protected function readPHPFile( $_fileName, $_fileType ) {
}
return $data;
}
+
/**
- * Read the plural rule xml files.
- * First the CLDR xml will be read and it will be extended with
- * mediawiki specific tailoring.
+ * Get the compiled plural rules for a given language from the XML files.
* @since 1.20
*/
- protected function readPluralRules() {
- $CLDRPlural = __DIR__ . "/../languages/data/plurals.xml";
- $MWPlural = __DIR__ . "/../languages/data/plurals-mediawiki.xml";
- # Load CLDR plural rules
- $this->parsePluralXML( $CLDRPlural );
- if ( file_exists( $MWPlural ) ) {
- // override or extend.
- $this->parsePluralXML( $MWPlural );
+ public function getCompiledPluralRules( $code ) {
+ $rules = $this->getPluralRules( $code );
+ if ( $rules === null ) {
+ return null;
+ }
+ try {
+ $compiledRules = CLDRPluralRuleEvaluator::compile( $rules );
+ } catch( CLDRPluralRuleError $e ) {
+ wfDebugLog( 'l10n', $e->getMessage() . "\n" );
+ return array();
}
+ return $compiledRules;
}
- private function parsePluralXML( $xmlFile ) {
- $pluraldoc = new DOMDocument();
- $pluraldoc->load( $xmlFile );
- $rulesets = $pluraldoc->getElementsByTagName( "pluralRules" );
+ /**
+ * Get the plural rules for a given language from the XML files.
+ * Cached.
+ * @since 1.20
+ */
+ public function getPluralRules( $code ) {
+ if ( $this->pluralRules === null ) {
+ $cldrPlural = __DIR__ . "/../languages/data/plurals.xml";
+ $mwPlural = __DIR__ . "/../languages/data/plurals-mediawiki.xml";
+ // Load CLDR plural rules
+ $this->loadPluralFile( $cldrPlural );
+ if ( file_exists( $mwPlural ) ) {
+ // Override or extend
+ $this->loadPluralFile( $mwPlural );
+ }
+ }
+ if ( !isset( $this->pluralRules[$code] ) ) {
+ return null;
+ } else {
+ return $this->pluralRules[$code];
+ }
+ }
+
+
+ /**
+ * Load a plural XML file with the given filename, compile the relevant
+ * rules, and save the compiled rules in a process-local cache.
+ */
+ protected function loadPluralFile( $fileName ) {
+ $doc = new DOMDocument;
+ $doc->load( $fileName );
+ $rulesets = $doc->getElementsByTagName( "pluralRules" );
foreach ( $rulesets as $ruleset ) {
$codes = $ruleset->getAttribute( 'locales' );
- $parsedRules = array();
- $rules = $ruleset->getElementsByTagName( "pluralRule" );
- foreach ( $rules as $rule ) {
- $parsedRules[$rule->getAttribute( 'count' )] = $rule->nodeValue;
+ $rules = array();
+ $ruleElements = $ruleset->getElementsByTagName( "pluralRule" );
+ foreach ( $ruleElements as $elt ) {
+ $rules[] = $elt->nodeValue;
}
foreach ( explode( ' ', $codes ) as $code ) {
- $this->pluralRules[$code] = $parsedRules;
+ $this->pluralRules[$code] = $rules;
}
}
}
+ /**
+ * Read the data from the source files for a given language, and register
+ * the relevant dependencies in the $deps array. If the localisation
+ * exists, the data array is returned, otherwise false is returned.
+ */
+ protected function readSourceFilesAndRegisterDeps( $code, &$deps ) {
+ $fileName = Language::getMessagesFileName( $code );
+ if ( !file_exists( $fileName ) ) {
+ return false;
+ }
+
+ $deps[] = new FileDependency( $fileName );
+ $data = $this->readPHPFile( $fileName, 'core' );
+
+ # Load CLDR plural rules for JavaScript
+ $data['pluralRules'] = $this->getPluralRules( $code );
+ # And for PHP
+ $data['compiledPluralRules'] = $this->getCompiledPluralRules( $code );
+
+ $deps['plurals'] = new FileDependency( __DIR__ . "/../languages/data/plurals.xml" );
+ $deps['plurals-mw'] = new FileDependency( __DIR__ . "/../languages/data/plurals-mediawiki.xml" );
+ return $data;
+ }
+
/**
* Merge two localisation values, a primary and a fallback, overwriting the
* primary value in place.
* Load localisation data for a given language for both core and extensions
* and save it to the persistent cache store and the process cache
* @param $code
+ * @throws MWException
*/
public function recache( $code ) {
global $wgExtensionMessagesFiles;
$deps = array();
# Load the primary localisation from the source file
- $fileName = Language::getMessagesFileName( $code );
- if ( !file_exists( $fileName ) ) {
+ $data = $this->readSourceFilesAndRegisterDeps( $code, $deps );
+ if ( $data === false ) {
wfDebug( __METHOD__ . ": no localisation file for $code, using fallback to en\n" );
$coreData['fallback'] = 'en';
} else {
- $deps[] = new FileDependency( $fileName );
- $data = $this->readPHPFile( $fileName, 'core' );
wfDebug( __METHOD__ . ": got localisation for $code from source\n" );
# Merge primary localisation
foreach ( $coreData['fallbackSequence'] as $fbCode ) {
# Load the secondary localisation from the source file to
# avoid infinite cycles on cyclic fallbacks
- $fbFilename = Language::getMessagesFileName( $fbCode );
-
- if ( !file_exists( $fbFilename ) ) {
+ $fbData = $this->readSourceFilesAndRegisterDeps( $fbCode, $deps );
+ if ( $fbData === false ) {
continue;
}
- $deps[] = new FileDependency( $fbFilename );
- $fbData = $this->readPHPFile( $fbFilename, 'core' );
-
foreach ( self::$allKeys as $key ) {
if ( !isset( $fbData[$key] ) ) {
continue;
# Decouple the reference to prevent accidental damage
unset( $page );
+ # If there were no plural rules, return an empty array
+ if ( $allData['pluralRules'] === null ) {
+ $allData['pluralRules'] = array();
+ }
+ if ( $allData['compiledPluralRules'] === null ) {
+ $allData['compiledPluralRules'] = array();
+ }
+
# Set the list keys
$allData['list'] = array();
foreach ( self::$splitKeys as $key ) {
$allData['list'][$key] = array_keys( $allData[$key] );
}
- # Load CLDR plural rules
- if ( isset( $this->pluralRules[$code] ) ) {
- $allData['pluralRules'] = $this->pluralRules[$code];
- }
# Run hooks
wfRunHooks( 'LocalisationCacheRecache', array( $this, $code, &$allData ) );
}
protected function getFileName( $code ) {
- if ( !$code || strpos( $code, '/' ) !== false ) {
+ if ( strval( $code ) === '' || strpos( $code, '/' ) !== false ) {
throw new MWException( __METHOD__ . ": Invalid language \"$code\"" );
}
return "{$this->directory}/l10n_cache-$code.cdb";