MediaWiki r91439 - Code Review

Jump to: navigation, search
Repository:MediaWiki
Revision:r91438‎ | r91439 (on ViewVC)‎ | r91440 >
Date:06:08, 5 July 2011
Author:jan
Status:ok (Comments)
Tags:
Comment:
Add PLURAL-support
Modified paths:

Diff [purge]

Index: trunk/tools/ToolserverI18N/TsIntuition.php
@@ -82,6 +82,9 @@
8383
8484 // Redirect address and status
8585 private $redirectTo = null;
 86+
 87+ // Object of MessagesFunctions
 88+ private $messagesFunctions = null;
8689
8790 /* Init
8891 * ------------------------------------------------- */
@@ -328,6 +331,20 @@
329332 return false;
330333 }
331334
 335+ /**
 336+ * Get a instance of MessagesFunctions
 337+ *
 338+ * @return Object of MessagesFunction
 339+ */
 340+ private function getMessagesFunctions() {
 341+ if ( $this->messagesFunctions == null ) {
 342+ require_once( $this->localBaseDir . '/language/MessagesFunctions.php' );
 343+ $this->messagesFunctions = MessagesFunctions::getInstance( $this->localBaseDir, $this );
 344+ return $this->messagesFunctions;
 345+ } else {
 346+ return $this->messagesFunctions;
 347+ }
 348+ }
332349
333350 /* Message functions
334351 * ------------------------------------------------- */
@@ -342,6 +359,7 @@
343360 * - lang: overrides the currently selected language
344361 * - variables: numerical array to do variable replacements ($1> var[0], $2> var[1], etc.)
345362 * - raw-variables: boolean to determine whether the variables should be escaped as well
 363+ * - parse: boolean to determine whether the message sould be parsed (PLURAL, etc.)
346364 * - escape: Optionally the return can be escaped. By default this takes place after variable
347365 * replacement. Set 'raw-variables' to true if you just want the raw message
348366 * to be escaped and have escaped the variables already.
@@ -364,6 +382,7 @@
365383 'variables' => array(),
366384 'raw-variables' => false,
367385 'escape' => 'plain',
 386+ 'parse' => false,
368387 );
369388
370389 // If $options was a domain string, convert it now.
@@ -418,6 +437,11 @@
419438 $n = $i + 1;
420439 $msg = str_replace( "\$$n", $val, $msg );
421440 }
 441+
 442+ // Some parsing work
 443+ if ( $options['parse'] === true ) {
 444+ $msg = $this->getMessagesFunctions()->parse( $msg, $lang );
 445+ }
422446
423447 // If not already escaped, do it now
424448 if ( !$escapeDone ) {
@@ -816,17 +840,25 @@
817841 * ------------------------------------------------- */
818842
819843 /**
820 - * @TODO:
 844+ * FIXME: Implement in language/MessagesFunctions.php.
 845+ *
 846+ * @todo
821847 */
822848 public function gender( $male, $female, $neutral ) {
823849 // Depends on getGender() which doesn't exist yet
 850+ throw new BadMethodCallException("Not supported yet!");
824851 }
825852
826853 /**
827 - * @TODO:
 854+ * Can be founded in language/MessagesFunctions.php.
 855+ *
 856+ * @see MessagesFunctions::parse
 857+ * @see MessagesFunctions::plural
 858+ * @deprecated
828859 */
829860 public function plural( $count, $forms ) {
830 - // Todo
 861+ throw new BadMethodCallException(
 862+ "Use msg() with \"parse\" option to support PLURAL!");
831863 }
832864
833865
@@ -1210,7 +1242,7 @@
12111243 }
12121244
12131245 // Custom version of trigger_error() that can be passed a custom filename and line number
1214 - private function errTrigger( $msg, $context, $level = E_WARNING, $file = '', $line = '' ) {
 1246+ public function errTrigger( $msg, $context, $level = E_WARNING, $file = '', $line = '' ) {
12151247 $die = false;
12161248 $error = false;
12171249 $notice = false;
Index: trunk/tools/ToolserverI18N/language/MessagesFunctions.php
@@ -0,0 +1,175 @@
 2+<?PHP
 3+/**
 4+ *
 5+ * Functions for messages (PLURAL, etc.).
 6+ *
 7+ * @package TsIntuition
 8+ * @author Jan Luca <jan@toolserver.org>
 9+ * @see TsIntuition::msg()
 10+ */
 11+
 12+class MessagesFunctions {
 13+ private static $instance = null;
 14+
 15+ private $langIsLoaded = array();
 16+
 17+ private $baseDir = null;
 18+
 19+ private $I18N = null;
 20+
 21+ private $pluralRegex = "@\\{\\{PLURAL\\:(.*?)\\|(.*?)\\}\\}@i";
 22+
 23+ private $error = array();
 24+
 25+ /**
 26+ *
 27+ * Get a instance of MessagesFunctions.
 28+ *
 29+ * @param String $baseDir The path of the root dir of TS-I18N
 30+ * @param TsIntuition $I18N The current object of TsIntution
 31+ * @static
 32+ * @see TsIntuition::getMessagesFunctions()
 33+ */
 34+ public static function getInstance( $baseDir, $I18N ) {
 35+ if( self::$instance == null ) {
 36+ self::$instance = new MessagesFunctions( $baseDir, $I18N );
 37+ return self::$instance;
 38+ } else {
 39+ return self::$instance;
 40+ }
 41+ }
 42+
 43+ /**
 44+ *
 45+ * Construct a new object of MessageFunctions.
 46+ *
 47+ * @param String $baseDir The path of the root dir of TS-I18N
 48+ * @param TsIntuition $I18N The current object of TsIntution
 49+ */
 50+ function __construct( $baseDir, $I18N ) {
 51+ $this->baseDir = $baseDir;
 52+ $this->I18N = $I18N;
 53+
 54+ require_once( $this->baseDir."/language/classes/Language.php" );
 55+ }
 56+
 57+ /**
 58+ *
 59+ * Load a language class file from its code.
 60+ *
 61+ * @param String $language Language-Code
 62+ */
 63+ private function loadLanguage( $language ) {
 64+ $language = ucfirst( strtolower( str_replace("-", "_", $language ) ) );
 65+
 66+ if ( in_array( $language, $this->langIsLoaded ) ) {
 67+ return;
 68+ }
 69+
 70+ $className = "Language".$language;
 71+
 72+ if ( file_exists( $this->baseDir."/language/classes/".$className.".php" ) ) {
 73+ include_once( $this->baseDir."/language/classes/".$className.".php" );
 74+ }
 75+
 76+ $this->langIsLoaded[] = $language;
 77+ }
 78+
 79+ /**
 80+ *
 81+ * Get the functions (PLURAL, etc.) from the message.
 82+ *
 83+ * @param String $msg the message with the functions
 84+ * @return array of the functions
 85+ */
 86+ private function getMsgFunctions( $msg ) {
 87+ $functions = array();
 88+
 89+ if ( preg_match_all($this->pluralRegex, $msg, $matches, PREG_SET_ORDER) ) {
 90+ foreach( $matches as $match ) {
 91+ $parameteres = array();
 92+ $parameteres[] = "plural";
 93+ $parameteres[] = trim($match[1]);
 94+
 95+ $forms = explode( "|", $match[2] );
 96+ foreach( $forms as $form ) {
 97+ $parameteres[] = $form;
 98+ }
 99+
 100+ $functions[] = $parameteres;
 101+ }
 102+ }
 103+
 104+ return $functions;
 105+ }
 106+
 107+ /**
 108+ *
 109+ * Parsing a message.
 110+ *
 111+ * @param String $msg Message
 112+ * @param String $lang Language of the message
 113+ * @return String Parsed message
 114+ */
 115+ public function parse( $msg, $lang ) {
 116+ $this->loadLanguage( $lang );
 117+
 118+ $msgFunctions = $this->getMsgFunctions( $msg );
 119+
 120+ foreach( $msgFunctions as $msgFunction ) {
 121+ $functionName = array_shift( $msgFunction );
 122+ $functionNumber = array_shift( $msgFunction );
 123+ if ( $functionNumber != null ) {
 124+ $msg = $this->$functionName( $functionNumber, $msgFunction, $msg, $lang );
 125+ } else {
 126+ $msg = $this->$functionName( $msgFunction, $msg, $lang );
 127+ }
 128+ }
 129+
 130+ $this->sendParseErrors( __METHOD__ );
 131+
 132+ return $msg;
 133+ }
 134+
 135+ private function plural( $number, $parameters, $msg, $language ) {
 136+ $language = ucfirst( strtolower( str_replace("-", "_", $language ) ) );
 137+
 138+ if ( !is_numeric( $number ) ) {
 139+ $this->addParseError( "Invalid number argument to {{PLURAL: ...}}",
 140+ __METHOD__, E_ERROR, __FILE__, __LINE__ );
 141+ return $msg;
 142+ }
 143+
 144+ $className = "Language".$language;
 145+
 146+ if ( class_exists( $className ) ) {
 147+ $langObj = new $className();
 148+ } else {
 149+ $langObj = new Language();
 150+ }
 151+
 152+ $form = $langObj->convertPlural( $number, $parameters );
 153+
 154+ $msg = preg_replace( $this->pluralRegex, $form, $msg, 1 );
 155+
 156+ return $msg;
 157+ }
 158+
 159+ private function addParseError( $errMsg, $context, $errType = E_WARNING, $file = '', $line = '' ) {
 160+ $this->error[] = array( "msg" => $errMsg, "context" => $context,
 161+ "type" => $errType, "file" => $file, "line" => $line );
 162+ }
 163+
 164+ private function sendParseErrors( $parseContext ) {
 165+ if ( count( $this->error ) < 1 ) return;
 166+
 167+ $this->I18N->errTrigger( "Problems when parsing the message. For Information see below!",
 168+ $parseContext, E_WARNING );
 169+
 170+ foreach( $this->error as $error ) {
 171+ $this->I18N->errTrigger( $error["msg"], $error["context"],
 172+ $error["type"], $error["file"], $error["line"] );
 173+ }
 174+ }
 175+}
 176+?>
\ No newline at end of file
Property changes on: trunk/tools/ToolserverI18N/language/MessagesFunctions.php
___________________________________________________________________
Added: svn:eol-style
1177 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHe.php
@@ -0,0 +1,70 @@
 2+<?php
 3+
 4+/**
 5+ * Hebrew (עברית)
 6+ *
 7+ * @ingroup Language
 8+ *
 9+ * @author Rotem Liss
 10+ */
 11+class LanguageHe extends Language {
 12+
 13+ /**
 14+ * Convert grammar forms of words.
 15+ *
 16+ * Available cases:
 17+ * "prefixed" (or "תחילית") - when the word has a prefix
 18+ *
 19+ * @param $word String: the word to convert
 20+ * @param $case String: the case
 21+ *
 22+ * @return string
 23+ */
 24+ public function convertGrammar( $word, $case ) {
 25+ global $wgGrammarForms;
 26+ if ( isset( $wgGrammarForms['he'][$case][$word] ) ) {
 27+ return $wgGrammarForms['he'][$case][$word];
 28+ }
 29+
 30+ switch ( $case ) {
 31+ case 'prefixed':
 32+ case 'תחילית':
 33+ # Duplicate the "Waw" if prefixed
 34+ if ( substr( $word, 0, 2 ) == "ו" && substr( $word, 0, 4 ) != "וו" ) {
 35+ $word = "ו" . $word;
 36+ }
 37+
 38+ # Remove the "He" if prefixed
 39+ if ( substr( $word, 0, 2 ) == "ה" ) {
 40+ $word = substr( $word, 2 );
 41+ }
 42+
 43+ # Add a hyphen if non-Hebrew letters
 44+ if ( substr( $word, 0, 2 ) < "א" || substr( $word, 0, 2 ) > "ת" ) {
 45+ $word = "־" . $word;
 46+ }
 47+ }
 48+
 49+ return $word;
 50+ }
 51+
 52+ /**
 53+ * Gets a number and uses the suited form of the word.
 54+ *
 55+ * @param $count Integer: the number of items
 56+ * @param $forms Array with 3 items: the three plural forms
 57+ * @return String: the suited form of word
 58+ */
 59+ function convertPlural( $count, $forms ) {
 60+ if ( !count( $forms ) ) { return ''; }
 61+ $forms = $this->preConvertPlural( $forms, 3 );
 62+
 63+ if ( $count == '1' ) {
 64+ return $forms[0];
 65+ } elseif ( $count == '2' && isset( $forms[2] ) ) {
 66+ return $forms[2];
 67+ } else {
 68+ return $forms[1];
 69+ }
 70+ }
 71+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHe.php
___________________________________________________________________
Added: svn:eol-style
172 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageLa.php
@@ -0,0 +1,83 @@
 2+<?php
 3+
 4+/** Latin (lingua Latina)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageLa extends Language {
 9+ /**
 10+ * Convert from the nominative form of a noun to some other case
 11+ *
 12+ * Just used in a couple places for sitenames; special-case as necessary.
 13+ * Rules are far from complete.
 14+ *
 15+ * Cases: genitive, accusative, ablative
 16+ *
 17+ * @param $word string
 18+ * @param $case string
 19+ *
 20+ * @return string
 21+ */
 22+ function convertGrammar( $word, $case ) {
 23+ global $wgGrammarForms;
 24+ if ( isset( $wgGrammarForms['la'][$case][$word] ) ) {
 25+ return $wgGrammarForms['la'][$case][$word];
 26+ }
 27+
 28+ switch ( $case ) {
 29+ case 'genitive':
 30+ // only a few declensions, and even for those mostly the singular only
 31+ $in = array( '/u[ms]$/', # 2nd declension singular
 32+ '/ommunia$/', # 3rd declension neuter plural (partly)
 33+ '/a$/', # 1st declension singular
 34+ '/libri$/', '/nuntii$/', # 2nd declension plural (partly)
 35+ '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly)
 36+ '/es$/' # 5th declension singular
 37+ );
 38+ $out = array( 'i',
 39+ 'ommunium',
 40+ 'ae',
 41+ 'librorum', 'nuntiorum',
 42+ 'tionis', 'ntis', 'atis',
 43+ 'ei'
 44+ );
 45+ return preg_replace( $in, $out, $word );
 46+ case 'accusative':
 47+ // only a few declensions, and even for those mostly the singular only
 48+ $in = array( '/u[ms]$/', # 2nd declension singular
 49+ '/a$/', # 1st declension singular
 50+ '/ommuniam$/', # 3rd declension neuter plural (partly)
 51+ '/libri$/', '/nuntii$/', # 2nd declension plural (partly)
 52+ '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly)
 53+ '/es$/' # 5th declension singular
 54+ );
 55+ $out = array( 'um',
 56+ 'am',
 57+ 'ommunia',
 58+ 'libros', 'nuntios',
 59+ 'tionem', 'ntem', 'atem',
 60+ 'em'
 61+ );
 62+ return preg_replace( $in, $out, $word );
 63+ case 'ablative':
 64+ // only a few declensions, and even for those mostly the singular only
 65+ $in = array( '/u[ms]$/', # 2nd declension singular
 66+ '/ommunia$/', # 3rd declension neuter plural (partly)
 67+ '/a$/', # 1st declension singular
 68+ '/libri$/', '/nuntii$/', # 2nd declension plural (partly)
 69+ '/tio$/', '/ns$/', '/as$/', # 3rd declension singular (partly)
 70+ '/es$/' # 5th declension singular
 71+ );
 72+ $out = array( 'o',
 73+ 'ommunibus',
 74+ 'a',
 75+ 'libris', 'nuntiis',
 76+ 'tione', 'nte', 'ate',
 77+ 'e'
 78+ );
 79+ return preg_replace( $in, $out, $word );
 80+ default:
 81+ return $word;
 82+ }
 83+ }
 84+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageLa.php
___________________________________________________________________
Added: svn:eol-style
185 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHi.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Hindi (हिन्दी)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageHi extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHi.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageIu.deps.php
@@ -0,0 +1,8 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageIu.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://mail.wikipedia.org/pipermail/wikitech-l/2006-January/033660.html
 8+
 9+require_once( dirname(__FILE__).'/../LanguageConverter.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageIu.deps.php
___________________________________________________________________
Added: svn:eol-style
110 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHr.php
@@ -0,0 +1,32 @@
 2+<?php
 3+/** Croatian (hrvatski)
 4+ *
 5+ * @ingroup Language
 6+ */
 7+
 8+class LanguageHr extends Language {
 9+
 10+ /**
 11+ * @param $count int
 12+ * @param $forms array
 13+ * @return string
 14+ */
 15+ function convertPlural( $count, $forms ) {
 16+ if ( !count( $forms ) ) { return ''; }
 17+ // @todo FIXME: CLDR defines 4 plural forms instead of 3. Plural for for decimals is missing.
 18+ // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 19+ $forms = $this->preConvertPlural( $forms, 3 );
 20+
 21+ if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) {
 22+ return $forms[2];
 23+ } else {
 24+ switch ( $count % 10 ) {
 25+ case 1: return $forms[0];
 26+ case 2:
 27+ case 3:
 28+ case 4: return $forms[1];
 29+ default: return $forms[2];
 30+ }
 31+ }
 32+ }
 33+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHr.php
___________________________________________________________________
Added: svn:eol-style
134 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageKu.deps.php
@@ -0,0 +1,9 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageKu.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://lists.wikimedia.org/pipermail/wikitech-l/2006-January/021311.html
 8+
 9+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
 10+require_once( dirname( __FILE__ ) . '/LanguageKu_ku.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageKu.deps.php
___________________________________________________________________
Added: svn:eol-style
111 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageLn.php
@@ -0,0 +1,23 @@
 2+<?php
 3+/**
 4+ * Lingala (Lingála)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageLn extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#ln
 12+ *
 13+ * @param $count int
 14+ * @param $forms array
 15+ *
 16+ * @return string
 17+ */
 18+ function convertPlural( $count, $forms ) {
 19+ if ( !count( $forms ) ) { return ''; }
 20+ $forms = $this->preConvertPlural( $forms, 2 );
 21+
 22+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 23+ }
 24+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageLn.php
___________________________________________________________________
Added: svn:eol-style
125 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageTg.php
@@ -0,0 +1,106 @@
 2+<?php
 3+
 4+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
 5+
 6+/**
 7+ * Converts Tajiki to latin orthography
 8+ * @ingroup Language
 9+ */
 10+class TgConverter extends LanguageConverter {
 11+ private $table = array(
 12+ 'а' => 'a',
 13+ 'б' => 'b',
 14+ 'в' => 'v',
 15+ 'г' => 'g',
 16+ 'д' => 'd',
 17+ 'е' => 'e',
 18+ 'ё' => 'jo',
 19+ 'ж' => 'ƶ',
 20+ 'з' => 'z',
 21+ 'ии ' => 'iji ',
 22+ 'и' => 'i',
 23+ 'й' => 'j',
 24+ 'к' => 'k',
 25+ 'л' => 'l',
 26+ 'м' => 'm',
 27+ 'н' => 'n',
 28+ 'о' => 'o',
 29+ 'п' => 'p',
 30+ 'р' => 'r',
 31+ 'с' => 's',
 32+ 'т' => 't',
 33+ 'у' => 'u',
 34+ 'ф' => 'f',
 35+ 'х' => 'x',
 36+ 'ч' => 'c',
 37+ 'ш' => 'ş',
 38+ 'ъ' => '\'',
 39+ 'э' => 'e',
 40+ 'ю' => 'ju',
 41+ 'я' => 'ja',
 42+ 'ғ' => 'ƣ',
 43+ 'ӣ' => 'ī',
 44+ 'қ' => 'q',
 45+ 'ӯ' => 'ū',
 46+ 'ҳ' => 'h',
 47+ 'ҷ' => 'ç',
 48+ 'ц' => 'ts',
 49+ 'А' => 'A',
 50+ 'Б' => 'B',
 51+ 'В' => 'V',
 52+ 'Г' => 'G',
 53+ 'Д' => 'D',
 54+ 'Е' => 'E',
 55+ 'Ё' => 'Jo',
 56+ 'Ж' => 'Ƶ',
 57+ 'З' => 'Z',
 58+ 'И' => 'I',
 59+ 'Й' => 'J',
 60+ 'К' => 'K',
 61+ 'Л' => 'L',
 62+ 'М' => 'M',
 63+ 'Н' => 'N',
 64+ 'О' => 'O',
 65+ 'П' => 'P',
 66+ 'Р' => 'R',
 67+ 'С' => 'S',
 68+ 'Т' => 'T',
 69+ 'У' => 'U',
 70+ 'Ф' => 'F',
 71+ 'Х' => 'X',
 72+ 'Ч' => 'C',
 73+ 'Ш' => 'Ş',
 74+ 'Ъ' => '\'',
 75+ 'Э' => 'E',
 76+ 'Ю' => 'Ju',
 77+ 'Я' => 'Ja',
 78+ 'Ғ' => 'Ƣ',
 79+ 'Ӣ' => 'Ī',
 80+ 'Қ' => 'Q',
 81+ 'Ӯ' => 'Ū',
 82+ 'Ҳ' => 'H',
 83+ 'Ҷ' => 'Ç',
 84+ 'Ц' => 'Ts',
 85+ );
 86+
 87+ function loadDefaultTables() {
 88+ $this->mTables = array(
 89+ 'tg-latn' => new ReplacementArray( $this->table ),
 90+ 'tg' => new ReplacementArray()
 91+ );
 92+ }
 93+
 94+}
 95+
 96+/**
 97+ * Tajik (Тоҷикӣ)
 98+ *
 99+ * @ingroup Language
 100+ */
 101+class LanguageTg extends Language {
 102+ function __construct() {
 103+ parent::__construct();
 104+ $variants = array( 'tg', 'tg-latn' );
 105+ $this->mConverter = new TgConverter( $this, 'tg', $variants );
 106+ }
 107+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageTg.php
___________________________________________________________________
Added: svn:eol-style
1108 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguagePl.php
@@ -0,0 +1,42 @@
 2+<?php
 3+
 4+/** Polish (polski)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguagePl extends Language {
 9+
 10+ /**
 11+ * @param $count string
 12+ * @param $forms array
 13+ * @return string
 14+ */
 15+ function convertPlural( $count, $forms ) {
 16+ if ( !count( $forms ) ) { return ''; }
 17+ $forms = $this->preConvertPlural( $forms, 3 );
 18+ $count = abs( $count );
 19+ if ( $count == 1 )
 20+ return $forms[0]; // singular
 21+ switch ( $count % 10 ) {
 22+ case 2:
 23+ case 3:
 24+ case 4:
 25+ if ( $count / 10 % 10 != 1 )
 26+ return $forms[1]; // plural
 27+ default:
 28+ return $forms[2]; // plural genitive
 29+ }
 30+ }
 31+
 32+ /**
 33+ * @param $_ string
 34+ * @return string
 35+ */
 36+ function commafy( $_ ) {
 37+ if ( !preg_match( '/^\-?\d{1,4}(\.\d+)?$/', $_ ) ) {
 38+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 39+ } else {
 40+ return $_;
 41+ }
 42+ }
 43+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguagePl.php
___________________________________________________________________
Added: svn:eol-style
144 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageTi.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Tigrinya (ትግርኛ)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageTi extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageTi.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHu.php
@@ -0,0 +1,29 @@
 2+<?php
 3+
 4+/** Hungarian localisation for MediaWiki
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageHu extends Language {
 9+
 10+ /**
 11+ * @param $word string
 12+ * @param $case
 13+ * @return string
 14+ */
 15+ function convertGrammar( $word, $case ) {
 16+ global $wgGrammarForms;
 17+ if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
 18+ return $wgGrammarForms[$this->getCode()][$case][$word];
 19+ }
 20+
 21+ switch ( $case ) {
 22+ case 'rol':
 23+ return $word . 'ról';
 24+ case 'ba':
 25+ return $word . 'ba';
 26+ case 'k':
 27+ return $word . 'k';
 28+ }
 29+ }
 30+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHu.php
___________________________________________________________________
Added: svn:eol-style
131 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageTl.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Tagalog (Tagalog)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageTl extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageTl.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageLt.php
@@ -0,0 +1,32 @@
 2+<?php
 3+
 4+/** Lithuanian (Lietuvių)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageLt extends Language {
 9+ /* Word forms (with examples):
 10+ 1 - vienas (1) lapas, dvidešimt vienas (21) lapas
 11+ 2 - trys (3) lapai
 12+ 3 - penkiolika (15) lapų
 13+ */
 14+
 15+ /**
 16+ * @param $count int
 17+ * @param $forms array
 18+ *
 19+ * @return string
 20+ */
 21+ function convertPlural( $count, $forms ) {
 22+ if ( !count( $forms ) ) { return ''; }
 23+
 24+ // if no number with word, then use $form[0] for singular and $form[1] for plural or zero
 25+ if ( count( $forms ) === 2 ) return $count == 1 ? $forms[0] : $forms[1];
 26+
 27+ $forms = $this->preConvertPlural( $forms, 3 );
 28+
 29+ if ( $count % 10 == 1 && $count % 100 != 11 ) return $forms[0];
 30+ if ( $count % 10 >= 2 && ( $count % 100 < 10 || $count % 100 >= 20 ) ) return $forms[1];
 31+ return $forms[2];
 32+ }
 33+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageLt.php
___________________________________________________________________
Added: svn:eol-style
134 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHy.php
@@ -0,0 +1,85 @@
 2+<?php
 3+
 4+/** Armenian (Հայերեն)
 5+ *
 6+ * @ingroup Language
 7+ * @author Ruben Vardanyan (Me@RubenVardanyan.com)
 8+ */
 9+class LanguageHy extends Language {
 10+
 11+ /**
 12+ * Convert from the nominative form of a noun to some other case
 13+ * Invoked with {{grammar:case|word}}
 14+ *
 15+ * @param $word string
 16+ * @param $case string
 17+ * @return string
 18+ */
 19+ function convertGrammar( $word, $case ) {
 20+ global $wgGrammarForms;
 21+ if ( isset( $wgGrammarForms['hy'][$case][$word] ) ) {
 22+ return $wgGrammarForms['hy'][$case][$word];
 23+ }
 24+
 25+ # These rules are not perfect, but they are currently only used for site names so it doesn't
 26+ # matter if they are wrong sometimes. Just add a special case for your site name if necessary.
 27+
 28+ # join and array_slice instead mb_substr
 29+ $ar = array();
 30+ preg_match_all( '/./us', $word, $ar );
 31+ if ( !preg_match( "/[a-zA-Z_]/us", $word ) )
 32+ switch ( $case ) {
 33+ case 'genitive': # սեռական հոլով
 34+ if ( join( '', array_slice( $ar[0], -1 ) ) == 'ա' )
 35+ $word = join( '', array_slice( $ar[0], 0, -1 ) ) . 'այի';
 36+ elseif ( join( '', array_slice( $ar[0], -1 ) ) == 'ո' )
 37+ $word = join( '', array_slice( $ar[0], 0, -1 ) ) . 'ոյի';
 38+ elseif ( join( '', array_slice( $ar[0], -4 ) ) == 'գիրք' )
 39+ $word = join( '', array_slice( $ar[0], 0, -4 ) ) . 'գրքի';
 40+ else
 41+ $word .= 'ի';
 42+ break;
 43+ case 'dative': # Տրական հոլով
 44+ # stub
 45+ break;
 46+ case 'accusative': # Հայցական հոլով
 47+ # stub
 48+ break;
 49+ case 'instrumental': #
 50+ # stub
 51+ break;
 52+ case 'prepositional': #
 53+ # stub
 54+ break;
 55+ }
 56+ return $word;
 57+ }
 58+
 59+ /**
 60+ * @param $count int
 61+ * @param $forms array
 62+ *
 63+ * @return string
 64+ */
 65+ function convertPlural( $count, $forms ) {
 66+ if ( !count( $forms ) ) { return ''; }
 67+ $forms = $this->preConvertPlural( $forms, 2 );
 68+
 69+ return ( abs( $count ) <= 1 ) ? $forms[0] : $forms[1];
 70+ }
 71+
 72+ /**
 73+ * Armenian numeric format is "12 345,67" but "1234,56"
 74+ *
 75+ * @param $_ string
 76+ *
 77+ * @return string
 78+ */
 79+ function commafy( $_ ) {
 80+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 81+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 82+ } else {
 83+ return $_;
 84+ }
 85+ }
 86+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHy.php
___________________________________________________________________
Added: svn:eol-style
187 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageLv.php
@@ -0,0 +1,31 @@
 2+<?php
 3+
 4+/** Latvian (Latviešu)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ * @author Niklas Laxström
 9+ *
 10+ * @copyright Copyright © 2006, Niklas Laxström
 11+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 12+ */
 13+class LanguageLv extends Language {
 14+ /**
 15+ * Plural form transformations. Using the first form for words with the last digit 1, but not for words with the last digits 11, and the second form for all the others.
 16+ *
 17+ * Example: {{plural:{{NUMBEROFARTICLES}}|article|articles}}
 18+ *
 19+ * @param $count Integer
 20+ * @param $forms Array
 21+ * @return String
 22+ */
 23+ function convertPlural( $count, $forms ) {
 24+ if ( !count( $forms ) ) { return ''; }
 25+
 26+ // @todo FIXME: CLDR defines 3 plural forms instead of 2. Form for 0 is missing.
 27+ // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#lv
 28+ $forms = $this->preConvertPlural( $forms, 2 );
 29+
 30+ return ( ( $count % 10 == 1 ) && ( $count % 100 != 11 ) ) ? $forms[0] : $forms[1];
 31+ }
 32+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageLv.php
___________________________________________________________________
Added: svn:eol-style
133 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageKsh.php
@@ -0,0 +1,182 @@
 2+<?php
 3+
 4+/** Ripuarian (Ripoarėsh)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ * @author Purodha Blissenbach
 9+ */
 10+class LanguageKsh extends Language {
 11+ static $familygender = array(
 12+ // Do not add male wiki families, since that's the default.
 13+ // No need add neuter wikis having names ending in -wiki.
 14+ 'wikipedia' => 'f',
 15+ 'wikiversity' => 'f',
 16+ 'wiktionary' => 'n',
 17+ 'wikibooks' => 'n',
 18+ 'wikiquote' => 'n',
 19+ 'wikisource' => 'n',
 20+ 'wikitravel' => 'n',
 21+ 'wikia' => 'f',
 22+ 'translatewiki.net' => 'n',
 23+ );
 24+
 25+ /**
 26+ * Convert from the nominative form of a noun to other cases.
 27+ * Invoked with {{GRAMMAR:case|word}} inside messages.
 28+ *
 29+ * case is a sequence of words, each of which is case insensitive.
 30+ * Between words, there must be at least one space character.
 31+ * Only the 1st character of each word is considered.
 32+ * Word order is irrelevant.
 33+ *
 34+ * Possible values specifying the grammatical case are:
 35+ * 1, Nominative
 36+ * 2, Genitive
 37+ * 3, Dative
 38+ * 4, Accusative, -omitted-
 39+ *
 40+ * Possible values specifying the article type are:
 41+ * Betoont focussed or stressed article
 42+ * -omitted- unstressed or unfocussed article
 43+ *
 44+ * Possible values for the type of genitive are:
 45+ * Sing, Iehr prepositioned genitive = possessive dative
 46+ * Vun, Fon, -omitted- postpositioned genitive
 47+ * = preposition "vun" with dative
 48+ *
 49+ * Values of case overrides & prepositions, in the order of preceedence:
 50+ * Sing, Iehr possessive dative = prepositioned genitive
 51+ * Vun, Fon preposition "vun" with dative
 52+ * = postpositioned genitive
 53+ * En, em preposition "en" with dative
 54+ *
 55+ * Values for object gender specifiers of the possessive dative, or
 56+ * prepositioned genitive, evaluated with "Sing, Iehr" of above only:
 57+ * Male a singular male object follows
 58+ * -omitted- a non-male or plural object follows
 59+ *
 60+ * We currently handle definite articles of the singular only.
 61+ * There is a full set of test cases at:
 62+ * http://translatewiki.net/wiki/Portal:Ksh#GRAMMAR_Pr%C3%B6%C3%B6fe
 63+ * Contents of the leftmost table column can be copied and pasted as
 64+ * "case" values.
 65+ *
 66+ * @param $word String
 67+ * @param $case String
 68+ *
 69+ * @return string
 70+ */
 71+ function convertGrammar( $word, $case ) {
 72+ $lord = strtolower( $word );
 73+ $gender = 'm'; // Nuutnaarel // default
 74+ if ( preg_match ( '/wiki$/', $lord ) ) {
 75+ $gender = 'n'; // Dat xyz-wiki
 76+ }
 77+ if ( isset( self::$familygender[$lord] ) ) {
 78+ $gender = self::$familygender[$lord];
 79+ }
 80+ $case = ' ' . strtolower( $case );
 81+ if ( preg_match( '/ [is]/', $case ) ) {
 82+ # däm WikiMaatplaz singe, dä Wikipeedija iere, däm Wikiwööterbooch singe
 83+ # dem/em WikiMaatplaz singe, de Wikipeedija iere, dem/em Wikiwööterbooch singe
 84+ # däm WikiMaatplaz sing, dä Wikipeedija ier, däm Wikiwööterbooch sing
 85+ # dem/em WikiMaatplaz sing, de Wikipeedija ier, dem/em Wikiwööterbooch sing
 86+ $word = ( preg_match( '/ b/', $case )
 87+ ? ( $gender=='f' ? 'dä' : 'däm' )
 88+ : ( $gender=='f' ? 'de' : 'dem' )
 89+ ) . ' ' . $word . ' ' .
 90+ ( $gender=='f' ? 'ier' : 'sing' ) .
 91+ ( preg_match( '/ m/', $case ) ? 'e' : ''
 92+ );
 93+ } elseif ( preg_match( '/ e/', $case ) ) {
 94+ # en dämm WikiMaatPlaz, en dä Wikipeedija, en dämm Wikiwööterbooch
 95+ # em WikiMaatplaz, en de Wikipeedija, em Wikiwööterbooch
 96+ if ( preg_match( '/ b/', $case ) ) {
 97+ $word = 'en '.( $gender == 'f' ? 'dä' : 'däm' ) . ' ' . $word;
 98+ } else {
 99+ $word = ( $gender == 'f' ? 'en de' : 'em' ) . ' ' . $word;
 100+ }
 101+ } elseif ( preg_match( '/ [fv]/', $case ) || preg_match( '/ [2jg]/', $case ) ) {
 102+ # vun däm WikiMaatplaz, vun dä Wikipeedija, vun däm Wikiwööterbooch
 103+ # vum WikiMaatplaz, vun de Wikipeedija, vum Wikiwööterbooch
 104+ if ( preg_match( '/ b/', $case ) ) {
 105+ $word = 'vun ' . ( $gender == 'f' ? 'dä' : 'däm' ) . ' ' . $word;
 106+ } else {
 107+ $word = ( $gender== 'f' ? 'vun de' : 'vum' ) . ' ' . $word;
 108+ }
 109+ } elseif ( preg_match( '/ [3d]/', $case ) ) {
 110+ # dämm WikiMaatPlaz, dä Wikipeedija, dämm Wikiwööterbooch
 111+ # dem/em WikiMaatplaz, de Wikipeedija, dem/em Wikiwööterbooch
 112+ if ( preg_match( '/ b/', $case ) ) {
 113+ $word = ( $gender == 'f' ? 'dää' : 'dämm' ) .' ' . $word;
 114+ } else {
 115+ $word = ( $gender == 'f' ? 'de' : 'dem' ) . ' ' . $word;
 116+ }
 117+ } else {
 118+ # dä WikiMaatPlaz, di Wikipeedija, dat Wikiwööterbooch
 119+ # der WikiMaatplaz, de Wikipeedija, et Wikiwööterbooch
 120+ if ( preg_match( '/ b/', $case ) ) {
 121+ switch ( $gender ) {
 122+ case 'm':
 123+ $lord = 'dä';
 124+ break ;
 125+ case 'f':
 126+ $lord = 'di';
 127+ break;
 128+ default:
 129+ $lord = 'dat';
 130+ }
 131+ } else {
 132+ switch ( $gender ) {
 133+ case 'm':
 134+ $lord = 'der';
 135+ break;
 136+ case 'f':
 137+ $lord = 'de';
 138+ break;
 139+ default:
 140+ $lord = 'et';
 141+ }
 142+ }
 143+ $word = $lord.' '.$word;
 144+ }
 145+ return $word;
 146+ }
 147+
 148+ /**
 149+ * Avoid grouping whole numbers between 0 to 9999
 150+ *
 151+ * @param $_ string
 152+ *
 153+ * @return string
 154+ */
 155+ public function commafy( $_ ) {
 156+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 157+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 158+ } else {
 159+ return $_;
 160+ }
 161+ }
 162+
 163+ /**
 164+ * Handle cases of (1, other, 0) or (1, other)
 165+ *
 166+ * @param $count int
 167+ * @param $forms array
 168+ *
 169+ * @return string
 170+ */
 171+ function convertPlural( $count, $forms ) {
 172+ if ( !count( $forms ) ) { return ''; }
 173+ $forms = $this->preConvertPlural( $forms, 3 );
 174+
 175+ if ( $count == 1 ) {
 176+ return $forms[0];
 177+ } elseif ( $count == 0 ) {
 178+ return $forms[2];
 179+ } else {
 180+ return $forms[1];
 181+ }
 182+ }
 183+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageKsh.php
___________________________________________________________________
Added: svn:eol-style
1184 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageTr.php
@@ -0,0 +1,65 @@
 2+<?php
 3+
 4+/**
 5+ * Turkish (Türkçe)
 6+ *
 7+ * Turkish has two different i, one with a dot and another without a dot. They
 8+ * are totally different letters in this language, so we have to override the
 9+ * ucfirst and lcfirst methods.
 10+ * See http://en.wikipedia.org/wiki/Dotted_and_dotless_I
 11+ * and @bug 28040
 12+ * @ingroup Language
 13+ */
 14+class LanguageTr extends Language {
 15+
 16+ /**
 17+ * @param $string string
 18+ * @return string
 19+ */
 20+ function ucfirst ( $string ) {
 21+ if ( !empty( $string ) && $string[0] == 'i' ) {
 22+ return 'İ' . substr( $string, 1 );
 23+ } else {
 24+ return parent::ucfirst( $string );
 25+ }
 26+ }
 27+
 28+ /**
 29+ * @param $string string
 30+ * @return mixed|string
 31+ */
 32+ function lcfirst ( $string ) {
 33+ if ( !empty( $string ) && $string[0] == 'I' ) {
 34+ return 'ı' . substr( $string, 1 );
 35+ } else {
 36+ return parent::lcfirst( $string );
 37+ }
 38+ }
 39+
 40+ /**
 41+ * @see bug 28040
 42+ *
 43+ * @param $string string
 44+ * @param $first string|bool
 45+ *
 46+ * @return string
 47+ */
 48+ function uc( $string, $first = false ) {
 49+ $string = preg_replace( '/i/', 'İ', $string );
 50+ return parent::uc( $string, $first );
 51+ }
 52+
 53+ /**
 54+ * @see bug 28040
 55+ *
 56+ * @param $string string
 57+ * @param $first string|bool
 58+ *
 59+ * @return string
 60+ */
 61+ function lc( $string, $first = false ) {
 62+ $string = preg_replace( '/I/', 'ı', $string );
 63+ return parent::lc( $string, $first );
 64+ }
 65+
 66+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageTr.php
___________________________________________________________________
Added: svn:eol-style
167 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageZh_hans.php
@@ -0,0 +1,47 @@
 2+<?php
 3+
 4+/**
 5+ * Simplified Chinese
 6+ *
 7+ * @ingroup Language
 8+ */
 9+class LanguageZh_hans extends Language {
 10+
 11+ /**
 12+ * @return bool
 13+ */
 14+ function hasWordBreaks() {
 15+ return false;
 16+ }
 17+
 18+ /**
 19+ * Eventually this should be a word segmentation;
 20+ * for now just treat each character as a word.
 21+ * @todo FIXME: Only do this for Han characters...
 22+ *
 23+ * @param $string string
 24+ *
 25+ * @return string
 26+ */
 27+ function segmentByWord( $string ) {
 28+ $reg = "/([\\xc0-\\xff][\\x80-\\xbf]*)/";
 29+ $s = self::insertSpace( $string, $reg );
 30+ return $s;
 31+ }
 32+
 33+ /**
 34+ * @param $s
 35+ * @return string
 36+ */
 37+ function normalizeForSearch( $s ) {
 38+ wfProfileIn( __METHOD__ );
 39+
 40+ // Double-width roman characters
 41+ $s = parent::normalizeForSearch( $s );
 42+ $s = trim( $s );
 43+ $s = $this->segmentByWord( $s );
 44+
 45+ wfProfileOut( __METHOD__ );
 46+ return $s;
 47+ }
 48+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageZh_hans.php
___________________________________________________________________
Added: svn:eol-style
149 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageSgs.php
@@ -0,0 +1,30 @@
 2+<?php
 3+/** Samogitian (Žemaitėška)
 4+ *
 5+ * @ingroup Language
 6+ *
 7+ * @author Niklas Laxström
 8+ */
 9+class LanguageSgs extends Language {
 10+
 11+ /**
 12+ * @param $count int
 13+ * @param $forms array
 14+ * @return string
 15+ */
 16+ function convertPlural( $count, $forms ) {
 17+ if ( !count( $forms ) ) { return ''; }
 18+ $forms = $this->preConvertPlural( $forms, 4 );
 19+
 20+ $count = abs( $count );
 21+ if ( $count === 0 || ( $count % 100 === 0 || ( $count % 100 >= 10 && $count % 100 < 20 ) ) ) {
 22+ return $forms[2];
 23+ } elseif ( $count % 10 === 1 ) {
 24+ return $forms[0];
 25+ } elseif ( $count % 10 === 2 ) {
 26+ return $forms[1];
 27+ } else {
 28+ return $forms[3];
 29+ }
 30+ }
 31+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageSgs.php
___________________________________________________________________
Added: svn:eol-style
132 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageAm.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Amharic (አማርኛ)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageAm extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageAm.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageSr_el.deps.php
@@ -0,0 +1,8 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageSr_el.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://mail.wikipedia.org/pipermail/wikitech-l/2006-January/033660.html
 8+
 9+require_once( dirname( __FILE__ ) . '/LanguageSr_ec.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageSr_el.deps.php
___________________________________________________________________
Added: svn:eol-style
110 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageAr.php
@@ -0,0 +1,54 @@
 2+<?php
 3+/** Arabic (العربية)
 4+ *
 5+ * @ingroup Language
 6+ *
 7+ * @author Niklas Laxström
 8+ */
 9+class LanguageAr extends Language {
 10+
 11+ /**
 12+ * @param $count int
 13+ * @param $forms array
 14+ * @return string
 15+ */
 16+ function convertPlural( $count, $forms ) {
 17+ if ( !count( $forms ) ) { return ''; }
 18+ $forms = $this->preConvertPlural( $forms, 6 );
 19+
 20+ if ( $count == 0 ) {
 21+ $index = 0;
 22+ } elseif ( $count == 1 ) {
 23+ $index = 1;
 24+ } elseif ( $count == 2 ) {
 25+ $index = 2;
 26+ } elseif ( $count % 100 >= 3 && $count % 100 <= 10 ) {
 27+ $index = 3;
 28+ } elseif ( $count % 100 >= 11 && $count % 100 <= 99 ) {
 29+ $index = 4;
 30+ } else {
 31+ $index = 5;
 32+ }
 33+ return $forms[$index];
 34+ }
 35+
 36+ /**
 37+ * Temporary hack for bug 9413: replace Arabic presentation forms with their
 38+ * standard equivalents.
 39+ *
 40+ * @todo FIXME: This is language-specific for now only to avoid the negative
 41+ * performance impact of enabling it for all languages.
 42+ *
 43+ * @param $s string
 44+ *
 45+ * @return string
 46+ */
 47+ function normalize( $s ) {
 48+ global $wgFixArabicUnicode;
 49+ $s = parent::normalize( $s );
 50+ if ( $wgFixArabicUnicode ) {
 51+ $s = $this->transformUsingPairFile( 'normalize-ar.ser', $s );
 52+ }
 53+ return $s;
 54+ }
 55+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageAr.php
___________________________________________________________________
Added: svn:eol-style
156 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMg.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Malagasy (Malagasy)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageMg extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMg.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageEo.php
@@ -0,0 +1,123 @@
 2+<?php
 3+
 4+/** Esperanto (Esperanto)
 5+ *
 6+ * @ingroup Language
 7+ * @author Brion Vibber <brion@pobox.com>
 8+ */
 9+class LanguageEo extends Language {
 10+ /**
 11+ * Wrapper for charset conversions.
 12+ *
 13+ * In most languages, this calls through to standard system iconv(), but
 14+ * for Esperanto we're also adding a special pseudo-charset to convert
 15+ * accented characters to/from the ASCII-friendly "X" surrogate coding:
 16+ *
 17+ * cx = ĉ cxx = cx
 18+ * gx = ĝ gxx = gx
 19+ * hx = ĥ hxx = hx
 20+ * jx = ĵ jxx = jx
 21+ * sx = ŝ sxx = sx
 22+ * ux = ŭ uxx = ux
 23+ * xx = x
 24+ *
 25+ * http://en.wikipedia.org/wiki/Esperanto_orthography#X-system
 26+ * http://eo.wikipedia.org/wiki/X-sistemo
 27+ *
 28+ * X-conversion is applied, in either direction, between "utf-8" and "x" charsets;
 29+ * this comes into effect when input is run through $wgRequest->getText() and the
 30+ * $wgEditEncoding is set to 'x'.
 31+ *
 32+ * In the long run, this should be moved out of here and into the client-side
 33+ * editor behavior; the original server-side translation system dates to 2002-2003
 34+ * when many browsers with really bad Unicode support were still in use.
 35+ *
 36+ * @param string $in input character set
 37+ * @param string $out output character set
 38+ * @param string $string text to be converted
 39+ * @return string
 40+ */
 41+ function iconv( $in, $out, $string ) {
 42+ if ( strcasecmp( $in, 'x' ) == 0 && strcasecmp( $out, 'utf-8' ) == 0 ) {
 43+ return preg_replace_callback (
 44+ '/([cghjsu]x?)((?:xx)*)(?!x)/i',
 45+ array( $this, 'strrtxuCallback' ), $string );
 46+ } elseif ( strcasecmp( $in, 'UTF-8' ) == 0 && strcasecmp( $out, 'x' ) == 0 ) {
 47+ # Double Xs only if they follow cxapelutaj literoj.
 48+ return preg_replace_callback(
 49+ '/((?:[cghjsu]|\xc4[\x88\x89\x9c\x9d\xa4\xa5\xb4\xb5]|\xc5[\x9c\x9d\xac\xad])x*)/i',
 50+ array( $this, 'strrtuxCallback' ), $string );
 51+ }
 52+ return parent::iconv( $in, $out, $string );
 53+ }
 54+
 55+ /**
 56+ * @param $matches array
 57+ * @return string
 58+ */
 59+ function strrtuxCallback( $matches ) {
 60+ static $ux = array (
 61+ 'x' => 'xx' , 'X' => 'Xx' ,
 62+ "\xc4\x88" => "Cx" , "\xc4\x89" => "cx" ,
 63+ "\xc4\x9c" => "Gx" , "\xc4\x9d" => "gx" ,
 64+ "\xc4\xa4" => "Hx" , "\xc4\xa5" => "hx" ,
 65+ "\xc4\xb4" => "Jx" , "\xc4\xb5" => "jx" ,
 66+ "\xc5\x9c" => "Sx" , "\xc5\x9d" => "sx" ,
 67+ "\xc5\xac" => "Ux" , "\xc5\xad" => "ux"
 68+ );
 69+ return strtr( $matches[1], $ux );
 70+ }
 71+
 72+ /**
 73+ * @param $matches array
 74+ * @return string
 75+ */
 76+ function strrtxuCallback( $matches ) {
 77+ static $xu = array (
 78+ 'xx' => 'x' , 'xX' => 'x' ,
 79+ 'Xx' => 'X' , 'XX' => 'X' ,
 80+ "Cx" => "\xc4\x88" , "CX" => "\xc4\x88" ,
 81+ "cx" => "\xc4\x89" , "cX" => "\xc4\x89" ,
 82+ "Gx" => "\xc4\x9c" , "GX" => "\xc4\x9c" ,
 83+ "gx" => "\xc4\x9d" , "gX" => "\xc4\x9d" ,
 84+ "Hx" => "\xc4\xa4" , "HX" => "\xc4\xa4" ,
 85+ "hx" => "\xc4\xa5" , "hX" => "\xc4\xa5" ,
 86+ "Jx" => "\xc4\xb4" , "JX" => "\xc4\xb4" ,
 87+ "jx" => "\xc4\xb5" , "jX" => "\xc4\xb5" ,
 88+ "Sx" => "\xc5\x9c" , "SX" => "\xc5\x9c" ,
 89+ "sx" => "\xc5\x9d" , "sX" => "\xc5\x9d" ,
 90+ "Ux" => "\xc5\xac" , "UX" => "\xc5\xac" ,
 91+ "ux" => "\xc5\xad" , "uX" => "\xc5\xad"
 92+ );
 93+ return strtr( $matches[1], $xu ) . strtr( $matches[2], $xu );
 94+ }
 95+
 96+ /**
 97+ * @param $s string
 98+ * @return string
 99+ */
 100+ function checkTitleEncoding( $s ) {
 101+ # Check for X-system backwards-compatibility URLs
 102+ $ishigh = preg_match( '/[\x80-\xff]/', $s );
 103+ $isutf = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
 104+ '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
 105+
 106+ if ( $ishigh and !$isutf ) {
 107+ # Assume Latin1
 108+ $s = utf8_encode( $s );
 109+ } else {
 110+ if ( preg_match( '/(\xc4[\x88\x89\x9c\x9d\xa4\xa5\xb4\xb5]' .
 111+ '|\xc5[\x9c\x9d\xac\xad])/', $s ) )
 112+ return $s;
 113+ }
 114+
 115+ // if( preg_match( '/[cghjsu]x/i', $s ) )
 116+ // return $this->iconv( 'x', 'utf-8', $s );
 117+ return $s;
 118+ }
 119+
 120+ function initEncoding() {
 121+ global $wgEditEncoding;
 122+ $wgEditEncoding = 'x';
 123+ }
 124+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageEo.php
___________________________________________________________________
Added: svn:eol-style
1125 + native
Index: trunk/tools/ToolserverI18N/language/classes/LICENSE.txt
@@ -0,0 +1,345 @@
 2+This directory is a copy of the languages/classes directory of MediaWiki. The files are under the "GNU GENERAL PUBLIC LICENSE Version 2" (see below).
 3+
 4+
 5+== GNU GENERAL PUBLIC LICENSE ==
 6+
 7+Version 2, June 1991
 8+
 9+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 10+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 11+Everyone is permitted to copy and distribute verbatim copies
 12+of this license document, but changing it is not allowed.
 13+
 14+=== Preamble ===
 15+
 16+The licenses for most software are designed to take away your
 17+freedom to share and change it. By contrast, the GNU General Public
 18+License is intended to guarantee your freedom to share and change free
 19+software--to make sure the software is free for all its users. This
 20+General Public License applies to most of the Free Software
 21+Foundation's software and to any other program whose authors commit to
 22+using it. (Some other Free Software Foundation software is covered by
 23+the GNU Library General Public License instead.) You can apply it to
 24+your programs, too.
 25+
 26+When we speak of free software, we are referring to freedom, not
 27+price. Our General Public Licenses are designed to make sure that you
 28+have the freedom to distribute copies of free software (and charge for
 29+this service if you wish), that you receive source code or can get it
 30+if you want it, that you can change the software or use pieces of it
 31+in new free programs; and that you know you can do these things.
 32+
 33+To protect your rights, we need to make restrictions that forbid
 34+anyone to deny you these rights or to ask you to surrender the rights.
 35+These restrictions translate to certain responsibilities for you if you
 36+distribute copies of the software, or if you modify it.
 37+
 38+For example, if you distribute copies of such a program, whether
 39+gratis or for a fee, you must give the recipients all the rights that
 40+you have. You must make sure that they, too, receive or can get the
 41+source code. And you must show them these terms so they know their
 42+rights.
 43+
 44+We protect your rights with two steps: (1) copyright the software, and
 45+(2) offer you this license which gives you legal permission to copy,
 46+distribute and/or modify the software.
 47+
 48+Also, for each author's protection and ours, we want to make certain
 49+that everyone understands that there is no warranty for this free
 50+software. If the software is modified by someone else and passed on, we
 51+want its recipients to know that what they have is not the original, so
 52+that any problems introduced by others will not reflect on the original
 53+authors' reputations.
 54+
 55+Finally, any free program is threatened constantly by software
 56+patents. We wish to avoid the danger that redistributors of a free
 57+program will individually obtain patent licenses, in effect making the
 58+program proprietary. To prevent this, we have made it clear that any
 59+patent must be licensed for everyone's free use or not licensed at all.
 60+
 61+The precise terms and conditions for copying, distribution and
 62+modification follow.
 63+
 64+== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ==
 65+
 66+'''0.''' This License applies to any program or other work which contains
 67+a notice placed by the copyright holder saying it may be distributed
 68+under the terms of this General Public License. The "Program", below,
 69+refers to any such program or work, and a "work based on the Program"
 70+means either the Program or any derivative work under copyright law:
 71+that is to say, a work containing the Program or a portion of it,
 72+either verbatim or with modifications and/or translated into another
 73+language. (Hereinafter, translation is included without limitation in
 74+the term "modification".) Each licensee is addressed as "you".
 75+
 76+Activities other than copying, distribution and modification are not
 77+covered by this License; they are outside its scope. The act of
 78+running the Program is not restricted, and the output from the Program
 79+is covered only if its contents constitute a work based on the
 80+Program (independent of having been made by running the Program).
 81+Whether that is true depends on what the Program does.
 82+
 83+'''1.''' You may copy and distribute verbatim copies of the Program's
 84+source code as you receive it, in any medium, provided that you
 85+conspicuously and appropriately publish on each copy an appropriate
 86+copyright notice and disclaimer of warranty; keep intact all the
 87+notices that refer to this License and to the absence of any warranty;
 88+and give any other recipients of the Program a copy of this License
 89+along with the Program.
 90+
 91+You may charge a fee for the physical act of transferring a copy, and
 92+you may at your option offer warranty protection in exchange for a fee.
 93+
 94+'''2.''' You may modify your copy or copies of the Program or any portion
 95+of it, thus forming a work based on the Program, and copy and
 96+distribute such modifications or work under the terms of Section 1
 97+above, provided that you also meet all of these conditions:
 98+
 99+ '''a)''' You must cause the modified files to carry prominent notices
 100+ stating that you changed the files and the date of any change.
 101+
 102+ '''b)''' You must cause any work that you distribute or publish, that in
 103+ whole or in part contains or is derived from the Program or any
 104+ part thereof, to be licensed as a whole at no charge to all third
 105+ parties under the terms of this License.
 106+
 107+ '''c)''' If the modified program normally reads commands interactively
 108+ when run, you must cause it, when started running for such
 109+ interactive use in the most ordinary way, to print or display an
 110+ announcement including an appropriate copyright notice and a
 111+ notice that there is no warranty (or else, saying that you provide
 112+ a warranty) and that users may redistribute the program under
 113+ these conditions, and telling the user how to view a copy of this
 114+ License. (Exception: if the Program itself is interactive but
 115+ does not normally print such an announcement, your work based on
 116+ the Program is not required to print an announcement.)
 117+
 118+These requirements apply to the modified work as a whole. If
 119+identifiable sections of that work are not derived from the Program,
 120+and can be reasonably considered independent and separate works in
 121+themselves, then this License, and its terms, do not apply to those
 122+sections when you distribute them as separate works. But when you
 123+distribute the same sections as part of a whole which is a work based
 124+on the Program, the distribution of the whole must be on the terms of
 125+this License, whose permissions for other licensees extend to the
 126+entire whole, and thus to each and every part regardless of who wrote it.
 127+
 128+Thus, it is not the intent of this section to claim rights or contest
 129+your rights to work written entirely by you; rather, the intent is to
 130+exercise the right to control the distribution of derivative or
 131+collective works based on the Program.
 132+
 133+In addition, mere aggregation of another work not based on the Program
 134+with the Program (or with a work based on the Program) on a volume of
 135+a storage or distribution medium does not bring the other work under
 136+the scope of this License.
 137+
 138+'''3.''' You may copy and distribute the Program (or a work based on it,
 139+under Section 2) in object code or executable form under the terms of
 140+Sections 1 and 2 above provided that you also do one of the following:
 141+
 142+ '''a)''' Accompany it with the complete corresponding machine-readable
 143+ source code, which must be distributed under the terms of Sections
 144+ 1 and 2 above on a medium customarily used for software interchange; or,
 145+
 146+ '''b)''' Accompany it with a written offer, valid for at least three
 147+ years, to give any third party, for a charge no more than your
 148+ cost of physically performing source distribution, a complete
 149+ machine-readable copy of the corresponding source code, to be
 150+ distributed under the terms of Sections 1 and 2 above on a medium
 151+ customarily used for software interchange; or,
 152+
 153+ '''c)''' Accompany it with the information you received as to the offer
 154+ to distribute corresponding source code. (This alternative is
 155+ allowed only for noncommercial distribution and only if you
 156+ received the program in object code or executable form with such
 157+ an offer, in accord with Subsection b above.)
 158+
 159+The source code for a work means the preferred form of the work for
 160+making modifications to it. For an executable work, complete source
 161+code means all the source code for all modules it contains, plus any
 162+associated interface definition files, plus the scripts used to
 163+control compilation and installation of the executable. However, as a
 164+special exception, the source code distributed need not include
 165+anything that is normally distributed (in either source or binary
 166+form) with the major components (compiler, kernel, and so on) of the
 167+operating system on which the executable runs, unless that component
 168+itself accompanies the executable.
 169+
 170+If distribution of executable or object code is made by offering
 171+access to copy from a designated place, then offering equivalent
 172+access to copy the source code from the same place counts as
 173+distribution of the source code, even though third parties are not
 174+compelled to copy the source along with the object code.
 175+
 176+'''4.''' You may not copy, modify, sublicense, or distribute the Program
 177+except as expressly provided under this License. Any attempt
 178+otherwise to copy, modify, sublicense or distribute the Program is
 179+void, and will automatically terminate your rights under this License.
 180+However, parties who have received copies, or rights, from you under
 181+this License will not have their licenses terminated so long as such
 182+parties remain in full compliance.
 183+
 184+'''5.''' You are not required to accept this License, since you have not
 185+signed it. However, nothing else grants you permission to modify or
 186+distribute the Program or its derivative works. These actions are
 187+prohibited by law if you do not accept this License. Therefore, by
 188+modifying or distributing the Program (or any work based on the
 189+Program), you indicate your acceptance of this License to do so, and
 190+all its terms and conditions for copying, distributing or modifying
 191+the Program or works based on it.
 192+
 193+'''6.''' Each time you redistribute the Program (or any work based on the
 194+Program), the recipient automatically receives a license from the
 195+original licensor to copy, distribute or modify the Program subject to
 196+these terms and conditions. You may not impose any further
 197+restrictions on the recipients' exercise of the rights granted herein.
 198+You are not responsible for enforcing compliance by third parties to
 199+this License.
 200+
 201+'''7.''' If, as a consequence of a court judgment or allegation of patent
 202+infringement or for any other reason (not limited to patent issues),
 203+conditions are imposed on you (whether by court order, agreement or
 204+otherwise) that contradict the conditions of this License, they do not
 205+excuse you from the conditions of this License. If you cannot
 206+distribute so as to satisfy simultaneously your obligations under this
 207+License and any other pertinent obligations, then as a consequence you
 208+may not distribute the Program at all. For example, if a patent
 209+license would not permit royalty-free redistribution of the Program by
 210+all those who receive copies directly or indirectly through you, then
 211+the only way you could satisfy both it and this License would be to
 212+refrain entirely from distribution of the Program.
 213+
 214+If any portion of this section is held invalid or unenforceable under
 215+any particular circumstance, the balance of the section is intended to
 216+apply and the section as a whole is intended to apply in other
 217+circumstances.
 218+
 219+It is not the purpose of this section to induce you to infringe any
 220+patents or other property right claims or to contest validity of any
 221+such claims; this section has the sole purpose of protecting the
 222+integrity of the free software distribution system, which is
 223+implemented by public license practices. Many people have made
 224+generous contributions to the wide range of software distributed
 225+through that system in reliance on consistent application of that
 226+system; it is up to the author/donor to decide if he or she is willing
 227+to distribute software through any other system and a licensee cannot
 228+impose that choice.
 229+
 230+This section is intended to make thoroughly clear what is believed to
 231+be a consequence of the rest of this License.
 232+
 233+'''8.''' If the distribution and/or use of the Program is restricted in
 234+certain countries either by patents or by copyrighted interfaces, the
 235+original copyright holder who places the Program under this License
 236+may add an explicit geographical distribution limitation excluding
 237+those countries, so that distribution is permitted only in or among
 238+countries not thus excluded. In such case, this License incorporates
 239+the limitation as if written in the body of this License.
 240+
 241+'''9.''' The Free Software Foundation may publish revised and/or new versions
 242+of the General Public License from time to time. Such new versions will
 243+be similar in spirit to the present version, but may differ in detail to
 244+address new problems or concerns.
 245+
 246+Each version is given a distinguishing version number. If the Program
 247+specifies a version number of this License which applies to it and "any
 248+later version", you have the option of following the terms and conditions
 249+either of that version or of any later version published by the Free
 250+Software Foundation. If the Program does not specify a version number of
 251+this License, you may choose any version ever published by the Free Software
 252+Foundation.
 253+
 254+'''10.''' If you wish to incorporate parts of the Program into other free
 255+programs whose distribution conditions are different, write to the author
 256+to ask for permission. For software which is copyrighted by the Free
 257+Software Foundation, write to the Free Software Foundation; we sometimes
 258+make exceptions for this. Our decision will be guided by the two goals
 259+of preserving the free status of all derivatives of our free software and
 260+of promoting the sharing and reuse of software generally.
 261+
 262+=== NO WARRANTY ===
 263+
 264+'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 265+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
 266+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 267+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 268+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 269+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
 270+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
 271+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 272+REPAIR OR CORRECTION.
 273+
 274+'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 275+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 276+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 277+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 278+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 279+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 280+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 281+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 282+POSSIBILITY OF SUCH DAMAGES.
 283+
 284+ '''END OF TERMS AND CONDITIONS'''
 285+
 286+== How to Apply These Terms to Your New Programs ==
 287+
 288+If you develop a new program, and you want it to be of the greatest
 289+possible use to the public, the best way to achieve this is to make it
 290+free software which everyone can redistribute and change under these terms.
 291+
 292+To do so, attach the following notices to the program. It is safest
 293+to attach them to the start of each source file to most effectively
 294+convey the exclusion of warranty; and each file should have at least
 295+the "copyright" line and a pointer to where the full notice is found.
 296+
 297+ <one line to give the program's name and a brief idea of what it does.>
 298+
 299+ Copyright (C) <year> <name of author>
 300+
 301+ This program is free software; you can redistribute it and/or modify
 302+ it under the terms of the GNU General Public License as published by
 303+ the Free Software Foundation; either version 2 of the License, or
 304+ (at your option) any later version.
 305+
 306+ This program is distributed in the hope that it will be useful,
 307+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 308+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 309+ GNU General Public License for more details.
 310+
 311+ You should have received a copy of the GNU General Public License
 312+ along with this program; if not, write to the Free Software
 313+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 314+
 315+
 316+Also add information on how to contact you by electronic and paper mail.
 317+
 318+If the program is interactive, make it output a short notice like this
 319+when it starts in an interactive mode:
 320+
 321+ Gnomovision version 69, Copyright (C) year name of author
 322+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 323+ This is free software, and you are welcome to redistribute it
 324+ under certain conditions; type `show c' for details.
 325+
 326+The hypothetical commands `show w' and `show c' should show the appropriate
 327+parts of the General Public License. Of course, the commands you use may
 328+be called something other than `show w' and `show c'; they could even be
 329+mouse-clicks or menu items--whatever suits your program.
 330+
 331+You should also get your employer (if you work as a programmer) or your
 332+school, if any, to sign a "copyright disclaimer" for the program, if
 333+necessary. Here is a sample; alter the names:
 334+
 335+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 336+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
 337+
 338+ <signature of Ty Coon>, 1 April 1989
 339+
 340+ Ty Coon, President of Vice
 341+
 342+This General Public License does not permit incorporating your program into
 343+proprietary programs. If your program is a subroutine library, you may
 344+consider it more useful to permit linking proprietary applications with the
 345+library. If this is what you want to do, use the GNU Library General
 346+Public License instead of this License.
Property changes on: trunk/tools/ToolserverI18N/language/classes/LICENSE.txt
___________________________________________________________________
Added: svn:eol-style
1347 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMk.php
@@ -0,0 +1,27 @@
 2+<?php
 3+/**
 4+ * Macedonian (Македонски)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageMk extends Language {
 9+ /**
 10+ * Plural forms per
 11+ * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#mk
 12+ *
 13+ * @param $count int
 14+ * @param $forms array
 15+ *
 16+ * @return string
 17+ */
 18+ function convertPlural( $count, $forms ) {
 19+ if ( !count( $forms ) ) { return ''; }
 20+ $forms = $this->preConvertPlural( $forms, 2 );
 21+
 22+ if ( $count % 10 === 1 && $count % 100 !== 11 ) {
 23+ return $forms[0];
 24+ } else {
 25+ return $forms[1];
 26+ }
 27+ }
 28+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMk.php
___________________________________________________________________
Added: svn:eol-style
129 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMl.php
@@ -0,0 +1,31 @@
 2+<?php
 3+
 4+/**
 5+ * Malayalam (മലയാളം)
 6+ *
 7+ * @ingroup Language
 8+ */
 9+class LanguageMl extends Language {
 10+ /**
 11+ * Temporary hack for the issue described at
 12+ * http://permalink.gmane.org/gmane.science.linguistics.wikipedia.technical/46396
 13+ * Convert Unicode 5.0 style Malayalam input to Unicode 5.1. Similar to
 14+ * bug 9413. Also fixes miscellaneous problems due to mishandling of ZWJ,
 15+ * e.g. bug 11162.
 16+ *
 17+ * @todo FIXME: This is language-specific for now only to avoid the negative
 18+ * performance impact of enabling it for all languages.
 19+ *
 20+ * @param $s string
 21+ *
 22+ * @return string
 23+ */
 24+ function normalize( $s ) {
 25+ global $wgFixMalayalamUnicode;
 26+ $s = parent::normalize( $s );
 27+ if ( $wgFixMalayalamUnicode ) {
 28+ $s = $this->transformUsingPairFile( 'normalize-ml.ser', $s );
 29+ }
 30+ return $s;
 31+ }
 32+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMl.php
___________________________________________________________________
Added: svn:eol-style
133 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageEt.php
@@ -0,0 +1,23 @@
 2+<?php
 3+
 4+/** Estonian (Eesti)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ */
 9+class LanguageEt extends Language {
 10+ /**
 11+ * Avoid grouping whole numbers between 0 to 9999
 12+ *
 13+ * @param $_ string
 14+ *
 15+ * @return string
 16+ */
 17+ function commafy( $_ ) {
 18+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 19+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 20+ } else {
 21+ return $_;
 22+ }
 23+ }
 24+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageEt.php
___________________________________________________________________
Added: svn:eol-style
125 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageAz.php
@@ -0,0 +1,19 @@
 2+<?php
 3+/** Azerbaijani (Azərbaycan)
 4+ *
 5+ * @ingroup Language
 6+ */
 7+class LanguageAz extends Language {
 8+
 9+ /**
 10+ * @param $string string
 11+ * @return mixed|string
 12+ */
 13+ function ucfirst ( $string ) {
 14+ if ( $string[0] == 'i' ) {
 15+ return 'İ' . substr( $string, 1 );
 16+ } else {
 17+ return parent::ucfirst( $string );
 18+ }
 19+ }
 20+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageAz.php
___________________________________________________________________
Added: svn:eol-style
121 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMo.php
@@ -0,0 +1,30 @@
 2+<?php
 3+/**
 4+ * Moldavian (Молдовеняскэ)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageMo extends Language {
 9+
 10+ /**
 11+ * @param $count int
 12+ * @param $forms array
 13+ * @return string
 14+ */
 15+ function convertPlural( $count, $forms ) {
 16+ // Plural rules per
 17+ // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#mo
 18+ if ( !count( $forms ) ) { return ''; }
 19+
 20+ $forms = $this->preConvertPlural( $forms, 3 );
 21+
 22+ if ( $count == 1 ) {
 23+ $index = 0;
 24+ } elseif ( $count == 0 || $count % 100 < 20 ) {
 25+ $index = 1;
 26+ } else {
 27+ $index = 2;
 28+ }
 29+ return $forms[$index];
 30+ }
 31+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMo.php
___________________________________________________________________
Added: svn:eol-style
132 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageZh.deps.php
@@ -0,0 +1,9 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageZh.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://lists.wikimedia.org/pipermail/wikitech-l/2006-January/021311.html
 8+
 9+require_once( dirname( __FILE__ ) . '/LanguageZh_hans.php' );
 10+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageZh.deps.php
___________________________________________________________________
Added: svn:eol-style
111 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageIu.php
@@ -0,0 +1,39 @@
 2+<?php
 3+/**
 4+ * @addtogroup Language
 5+ */
 6+
 7+/*
 8+* Conversion script between Latin and Syllabics for Inuktitut.
 9+* - Syllabics -> lowercase Latin
 10+* - lowercase/uppercase Latin -> Syllabics
 11+*
 12+*
 13+* Based on:
 14+* - http://commons.wikimedia.org/wiki/Image:Inuktitut.png
 15+* - LanguageSr.php
 16+*
 17+* @ingroup Language
 18+*/
 19+
 20+/**
 21+ * Inuktitut
 22+ *
 23+ * @ingroup Language
 24+ */
 25+class LanguageIu extends Language {
 26+ function __construct() {
 27+
 28+
 29+ parent::__construct();
 30+
 31+ $variants = array( 'iu', 'ike-cans', 'ike-latn' );
 32+ $variantfallbacks = array(
 33+ 'iu' => 'ike-cans',
 34+ 'ike-cans' => 'iu',
 35+ 'ike-latn' => 'iu',
 36+ );
 37+
 38+ $flags = array();
 39+ }
 40+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageIu.php
___________________________________________________________________
Added: svn:eol-style
141 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageSr.deps.php
@@ -0,0 +1,9 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageSr.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://lists.wikimedia.org/pipermail/wikitech-l/2006-January/021311.html
 8+
 9+require_once( dirname( __FILE__ ) . '/LanguageSr_ec.php' );
 10+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageSr.deps.php
___________________________________________________________________
Added: svn:eol-style
111 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageUk.php
@@ -0,0 +1,108 @@
 2+<?php
 3+
 4+/** Ukrainian (українська мова)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageUk extends Language {
 9+
 10+ /**
 11+ * Convert from the nominative form of a noun to some other case
 12+ * Invoked with {{grammar:case|word}}
 13+ *
 14+ * @param $word string
 15+ * @param $case string
 16+ * @return string
 17+ */
 18+ function convertGrammar( $word, $case ) {
 19+ global $wgGrammarForms;
 20+ if ( isset( $wgGrammarForms['uk'][$case][$word] ) ) {
 21+ return $wgGrammarForms['uk'][$case][$word];
 22+ }
 23+
 24+ # These rules are not perfect, but they are currently only used for site names so it doesn't
 25+ # matter if they are wrong sometimes. Just add a special case for your site name if necessary.
 26+
 27+ # join and array_slice instead mb_substr
 28+ $ar = array();
 29+ preg_match_all( '/./us', $word, $ar );
 30+ if ( !preg_match( "/[a-zA-Z_]/us", $word ) )
 31+ switch ( $case ) {
 32+ case 'genitive': # родовий відмінок
 33+ if ( ( join( '', array_slice( $ar[0], -4 ) ) == 'вікі' ) || ( join( '', array_slice( $ar[0], -4 ) ) == 'Вікі' ) )
 34+ { }
 35+ elseif ( join( '', array_slice( $ar[0], -1 ) ) == 'ь' )
 36+ $word = join( '', array_slice( $ar[0], 0, -1 ) ) . 'я';
 37+ elseif ( join( '', array_slice( $ar[0], -2 ) ) == 'ія' )
 38+ $word = join( '', array_slice( $ar[0], 0, -2 ) ) . 'ії';
 39+ elseif ( join( '', array_slice( $ar[0], -2 ) ) == 'ка' )
 40+ $word = join( '', array_slice( $ar[0], 0, -2 ) ) . 'ки';
 41+ elseif ( join( '', array_slice( $ar[0], -2 ) ) == 'ти' )
 42+ $word = join( '', array_slice( $ar[0], 0, -2 ) ) . 'тей';
 43+ elseif ( join( '', array_slice( $ar[0], -2 ) ) == 'ди' )
 44+ $word = join( '', array_slice( $ar[0], 0, -2 ) ) . 'дів';
 45+ elseif ( join( '', array_slice( $ar[0], -3 ) ) == 'ник' )
 46+ $word = join( '', array_slice( $ar[0], 0, -3 ) ) . 'ника';
 47+ break;
 48+ case 'dative': # давальний відмінок
 49+ # stub
 50+ break;
 51+ case 'accusative': # знахідний відмінок
 52+ if ( ( join( '', array_slice( $ar[0], -4 ) ) == 'вікі' ) || ( join( '', array_slice( $ar[0], -4 ) ) == 'Вікі' ) )
 53+ { }
 54+ elseif ( join( '', array_slice( $ar[0], -2 ) ) == 'ія' )
 55+ $word = join( '', array_slice( $ar[0], 0, -2 ) ) . 'ію';
 56+ break;
 57+ case 'instrumental': # орудний відмінок
 58+ # stub
 59+ break;
 60+ case 'prepositional': # місцевий відмінок
 61+ # stub
 62+ break;
 63+ }
 64+ return $word;
 65+ }
 66+
 67+ /**
 68+ * @param $count int
 69+ * @param $forms array
 70+ * @return string
 71+ */
 72+ function convertPlural( $count, $forms ) {
 73+ if ( !count( $forms ) ) { return ''; }
 74+
 75+ // if no number with word, then use $form[0] for singular and $form[1] for plural or zero
 76+ if ( count( $forms ) === 2 ) return $count == 1 ? $forms[0] : $forms[1];
 77+
 78+ // @todo FIXME: CLDR defines 4 plural forms. Form for decimals is missing/
 79+ // See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html#uk
 80+ $forms = $this->preConvertPlural( $forms, 3 );
 81+
 82+ if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) {
 83+ return $forms[2];
 84+ } else {
 85+ switch ( $count % 10 ) {
 86+ case 1: return $forms[0];
 87+ case 2:
 88+ case 3:
 89+ case 4: return $forms[1];
 90+ default: return $forms[2];
 91+ }
 92+ }
 93+ }
 94+
 95+ /**
 96+ * Ukrainian numeric format is "12 345,67" but "1234,56"
 97+ *
 98+ * @param $_ string
 99+ *
 100+ * @return string
 101+ */
 102+ function commafy( $_ ) {
 103+ if ( !preg_match( '/^\-?\d{1,4}(\.\d+)?$/', $_ ) ) {
 104+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 105+ } else {
 106+ return $_;
 107+ }
 108+ }
 109+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageUk.php
___________________________________________________________________
Added: svn:eol-style
1110 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMt.php
@@ -0,0 +1,28 @@
 2+<?php
 3+
 4+/** Maltese (Malti)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ * @author Niklas Laxström
 9+ */
 10+
 11+class LanguageMt extends Language {
 12+
 13+ /**
 14+ * @param $count int
 15+ * @param $forms array
 16+ * @return string
 17+ */
 18+ function convertPlural( $count, $forms ) {
 19+ if ( !count( $forms ) ) { return ''; }
 20+
 21+ $forms = $this->preConvertPlural( $forms, 4 );
 22+
 23+ if ( $count === 1 ) $index = 0;
 24+ elseif ( $count === 0 || ( $count % 100 > 1 && $count % 100 < 11 ) ) $index = 1;
 25+ elseif ( $count % 100 > 10 && $count % 100 < 20 ) $index = 2;
 26+ else $index = 3;
 27+ return $forms[$index];
 28+ }
 29+}
\ No newline at end of file
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMt.php
___________________________________________________________________
Added: svn:eol-style
130 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageTyv.php
@@ -0,0 +1,226 @@
 2+<?php
 3+
 4+/** Tyvan localization (Тыва дыл)
 5+ * From friends at tyvawiki.org
 6+ *
 7+ * @ingroup Language
 8+ */
 9+class LanguageTyv extends Language {
 10+ /**
 11+ * Grammatical transformations, needed for inflected languages
 12+ * Invoked by putting {{grammar:case|word}} in a message
 13+ *
 14+ * @param $word string
 15+ * @param $case string
 16+ * @return string
 17+ */
 18+ function convertGrammar( $word, $case ) {
 19+ global $wgGrammarForms;
 20+ if ( isset( $wgGrammarForms['tyv'][$case][$word] ) ) {
 21+ return $wgGrammarForms['tyv'][$case][$word];
 22+ }
 23+
 24+ // Set up some constants...
 25+ $allVowels = array( "е", "и", "э", "ө", "ү", "а", "ё", "о", "у", "ы", "ю", "я", "a", "e", "i", "o", "ö", "u", "ü", "y" );
 26+ $frontVowels = array( "е", "и", "э", "ө", "ү", "e", "i", "ö", "ü" );
 27+ $backVowels = array( "а", "ё", "о", "у", "ы", "ю", "я", "a", "o", "u", "y" );
 28+ $unroundFrontVowels = array( "е", "и", "э", "e", "i" );
 29+ $roundFrontVowels = array( "ө", "ү", "ö", "ü" );
 30+ $unroundBackVowels = array( "а", "ы", "я", "a", "y" );
 31+ $roundBackVowels = array( "ё", "о", "у", "ю", "o", "u" );
 32+ //$voicedPhonemes = array( "д", "б", "з", "ж", "г", "d", "b", "z", "g" );
 33+ $unvoicedPhonemes = array( "т", "п", "с", "ш", "к", "ч", "х", "t", "p", "s", "k", "x" );
 34+ $directiveUnvoicedStems = array( "т", "п", "с", "ш", "к", "ч", "х", "л", "м", "н", "ң", "t", "p", "s", "k", "x", "l", "m", "n", "ŋ" );
 35+ $directiveVoicedStems = array( "д", "б", "з", "ж", "г", "р", "й", "d", "b", "z", "g", "r", "j" );
 36+
 37+ //$allSonants = array("л", "м", "н", "ң", "р", "й");
 38+ //$allNasals = array("м", "н", "ң");
 39+
 40+ //Put the word in a form we can play with since we're using UTF-8
 41+ preg_match_all( '/./us', $word, $ar );
 42+
 43+ $wordEnding = $ar[0][count( $ar[0] ) - 1]; // Here's the last letter in the word
 44+ $wordReversed = array_reverse( $ar[0] ); // Here's an array with the order of the letters in the word reversed so we can find a match quicker *shrug*
 45+
 46+ // Find the last vowel in the word
 47+ $wordLastVowel = NULL;
 48+ foreach ( $wordReversed as $xvalue ) {
 49+ foreach ( $allVowels as $yvalue ) {
 50+ if ( strcmp( $xvalue, $yvalue ) == 0 ) {
 51+ $wordLastVowel = $xvalue;
 52+ break;
 53+ } else {
 54+ continue;
 55+ }
 56+ }
 57+ if ( $wordLastVowel !== NULL ) {
 58+ break;
 59+ } else {
 60+ continue;
 61+ }
 62+ }
 63+
 64+ // Now convert the word
 65+ switch ( $case ) {
 66+ case "genitive":
 67+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 68+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 69+ $word = implode( "", $ar[0] ) . "түң";
 70+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 71+ $word = implode( "", $ar[0] ) . "тиң";
 72+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 73+ $word = implode( "", $ar[0] ) . "туң";
 74+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 75+ $word = implode( "", $ar[0] ) . "тың";
 76+ } else {
 77+ }
 78+ } elseif ( $wordEnding === "л" || $wordEnding === "l" ) {
 79+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 80+ $word = implode( "", $ar[0] ) . "дүң";
 81+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 82+ $word = implode( "", $ar[0] ) . "диң";
 83+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 84+ $word = implode( "", $ar[0] ) . "дуң";
 85+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 86+ $word = implode( "", $ar[0] ) . "дың";
 87+ } else {
 88+ }
 89+ } else {
 90+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 91+ $word = implode( "", $ar[0] ) . "нүң";
 92+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 93+ $word = implode( "", $ar[0] ) . "ниң";
 94+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 95+ $word = implode( "", $ar[0] ) . "нуң";
 96+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 97+ $word = implode( "", $ar[0] ) . "ның";
 98+ } else {
 99+ }
 100+ }
 101+ break;
 102+ case "dative":
 103+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 104+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 105+ $word = implode( "", $ar[0] ) . "ке";
 106+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 107+ $word = implode( "", $ar[0] ) . "ка";
 108+ } else {
 109+ }
 110+ } else {
 111+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 112+ $word = implode( "", $ar[0] ) . "ге";
 113+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 114+ $word = implode( "", $ar[0] ) . "га";
 115+ } else {
 116+ }
 117+ }
 118+ break;
 119+ case "accusative":
 120+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 121+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 122+ $word = implode( "", $ar[0] ) . "тү";
 123+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 124+ $word = implode( "", $ar[0] ) . "ти";
 125+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 126+ $word = implode( "", $ar[0] ) . "ту";
 127+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 128+ $word = implode( "", $ar[0] ) . "ты";
 129+ } else {
 130+ }
 131+ } elseif ( $wordEnding === "л" || $wordEnding === "l" ) {
 132+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 133+ $word = implode( "", $ar[0] ) . "дү";
 134+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 135+ $word = implode( "", $ar[0] ) . "ди";
 136+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 137+ $word = implode( "", $ar[0] ) . "ду";
 138+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 139+ $word = implode( "", $ar[0] ) . "ды";
 140+ } else {
 141+ }
 142+ } else {
 143+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 144+ $word = implode( "", $ar[0] ) . "нү";
 145+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 146+ $word = implode( "", $ar[0] ) . "ни";
 147+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 148+ $word = implode( "", $ar[0] ) . "ну";
 149+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 150+ $word = implode( "", $ar[0] ) . "ны";
 151+ } else {
 152+ }
 153+ }
 154+ break;
 155+ case "locative":
 156+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 157+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 158+ $word = implode( "", $ar[0] ) . "те";
 159+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 160+ $word = implode( "", $ar[0] ) . "та";
 161+ } else {
 162+ }
 163+ } else {
 164+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 165+ $word = implode( "", $ar[0] ) . "де";
 166+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 167+ $word = implode( "", $ar[0] ) . "да";
 168+ } else {
 169+ }
 170+ }
 171+ break;
 172+ case "ablative":
 173+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 174+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 175+ $word = implode( "", $ar[0] ) . "тен";
 176+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 177+ $word = implode( "", $ar[0] ) . "тан";
 178+ } else {
 179+ }
 180+ } else {
 181+ if ( in_array( $wordLastVowel, $frontVowels ) ) {
 182+ $word = implode( "", $ar[0] ) . "ден";
 183+ } elseif ( in_array( $wordLastVowel, $backVowels ) ) {
 184+ $word = implode( "", $ar[0] ) . "дан";
 185+ } else {
 186+ }
 187+ }
 188+ break;
 189+ case "directive1":
 190+ if ( in_array( $wordEnding, $directiveVoicedStems ) ) {
 191+ $word = implode( "", $ar[0] ) . "же";
 192+ } elseif ( in_array( $wordEnding, $directiveUnvoicedStems ) ) {
 193+ $word = implode( "", $ar[0] ) . "че";
 194+ } else {
 195+ }
 196+ break;
 197+ case "directive2":
 198+ if ( in_array( $wordEnding, $unvoicedPhonemes ) ) {
 199+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 200+ $word = implode( "", $ar[0] ) . "түве";
 201+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 202+ $word = implode( "", $ar[0] ) . "тиве";
 203+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 204+ $word = implode( "", $ar[0] ) . "туве";
 205+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 206+ $word = implode( "", $ar[0] ) . "тыве";
 207+ } else {
 208+ }
 209+ } else {
 210+ if ( in_array( $wordLastVowel, $roundFrontVowels ) ) {
 211+ $word = implode( "", $ar[0] ) . "дүве";
 212+ } elseif ( in_array( $wordLastVowel, $unroundFrontVowels ) ) {
 213+ $word = implode( "", $ar[0] ) . "диве";
 214+ } elseif ( in_array( $wordLastVowel, $roundBackVowels ) ) {
 215+ $word = implode( "", $ar[0] ) . "дуве";
 216+ } elseif ( in_array( $wordLastVowel, $unroundBackVowels ) ) {
 217+ $word = implode( "", $ar[0] ) . "дыве";
 218+ } else {
 219+ }
 220+ }
 221+ break;
 222+ default:
 223+ break;
 224+ }
 225+ return $word;
 226+ }
 227+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageTyv.php
___________________________________________________________________
Added: svn:eol-style
1228 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageMy.php
@@ -0,0 +1,22 @@
 2+<?php
 3+
 4+/** Burmese (Myanmasa)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ * @author Niklas Laxström, 2008
 9+ */
 10+class LanguageMy extends Language {
 11+
 12+ /**
 13+ * @param $_ string
 14+ * @return string
 15+ */
 16+ function commafy( $_ ) {
 17+ /* NO-op. Cannot use
 18+ * $separatorTransformTable = array( ',' => '' )
 19+ * That would break when parsing and doing strstr '' => 'foo';
 20+ */
 21+ return $_;
 22+ }
 23+}
\ No newline at end of file
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageMy.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageBe.php
@@ -0,0 +1,41 @@
 2+<?php
 3+/** Belarusian normative (Беларуская мова)
 4+ *
 5+ * This is still the version from Be-x-old, only duplicated for consistency of
 6+ * plural and grammar functions. If there are errors please send a patch.
 7+ *
 8+ * @ingroup Language
 9+ *
 10+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
 11+ * @link http://be.wikipedia.org/wiki/Talk:LanguageBe.php
 12+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
 13+ * @license http://www.gnu.org/copyleft/fdl.html GNU Free Documentation License
 14+ */
 15+
 16+class LanguageBe extends Language {
 17+
 18+ /**
 19+ * @param $count int
 20+ * @param $forms array
 21+ *
 22+ * @return string
 23+ */
 24+ function convertPlural( $count, $forms ) {
 25+ if ( !count( $forms ) ) { return ''; }
 26+ // @todo FIXME: CLDR defines 4 plural forms instead of 3
 27+ // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 28+ $forms = $this->preConvertPlural( $forms, 3 );
 29+
 30+ if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) {
 31+ return $forms[2];
 32+ } else {
 33+ switch ( $count % 10 ) {
 34+ case 1: return $forms[0];
 35+ case 2:
 36+ case 3:
 37+ case 4: return $forms[1];
 38+ default: return $forms[2];
 39+ }
 40+ }
 41+ }
 42+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageBe.php
___________________________________________________________________
Added: svn:eol-style
143 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageBg.php
@@ -0,0 +1,23 @@
 2+<?php
 3+
 4+/** Bulgarian (Български)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageBg extends Language {
 9+ /**
 10+ * ISO number formatting: 123 456 789,99.
 11+ * Avoid tripple grouping by numbers with whole part up to 4 digits.
 12+ *
 13+ * @param $_ string
 14+ *
 15+ * @return string
 16+ */
 17+ function commafy( $_ ) {
 18+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 19+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 20+ } else {
 21+ return $_;
 22+ }
 23+ }
 24+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageBg.php
___________________________________________________________________
Added: svn:eol-style
125 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageBh.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Bihari (भोजपुरी)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageBh extends Language {
 9+ /**
 10+ * Use singular form for zero
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageBh.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageJa.php
@@ -0,0 +1,41 @@
 2+<?php
 3+
 4+/**
 5+ * Japanese (日本語)
 6+ *
 7+ * @ingroup Language
 8+ */
 9+class LanguageJa extends Language {
 10+
 11+ /**
 12+ * @param $string string
 13+ * @return string
 14+ */
 15+ function segmentByWord( $string ) {
 16+ // Strip known punctuation ?
 17+ // $s = preg_replace( '/\xe3\x80[\x80-\xbf]/', '', $s ); # U3000-303f
 18+
 19+ // Space strings of like hiragana/katakana/kanji
 20+ $hiragana = '(?:\xe3(?:\x81[\x80-\xbf]|\x82[\x80-\x9f]))'; # U3040-309f
 21+ $katakana = '(?:\xe3(?:\x82[\xa0-\xbf]|\x83[\x80-\xbf]))'; # U30a0-30ff
 22+ $kanji = '(?:\xe3[\x88-\xbf][\x80-\xbf]'
 23+ . '|[\xe4-\xe8][\x80-\xbf]{2}'
 24+ . '|\xe9[\x80-\xa5][\x80-\xbf]'
 25+ . '|\xe9\xa6[\x80-\x99])';
 26+ # U3200-9999 = \xe3\x88\x80-\xe9\xa6\x99
 27+ $reg = "/({$hiragana}+|{$katakana}+|{$kanji}+)/";
 28+ $s = self::insertSpace( $string, $reg );
 29+ return $s;
 30+ }
 31+
 32+ /**
 33+ * Italic is not appropriate for Japanese script
 34+ * Unfortunately most browsers do not recognise this, and render <em> as italic
 35+ *
 36+ * @param $text string
 37+ * @return string
 38+ */
 39+ function emphasize( $text ) {
 40+ return $text;
 41+ }
 42+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageJa.php
___________________________________________________________________
Added: svn:eol-style
143 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageKaa.php
@@ -0,0 +1,73 @@
 2+<?php
 3+
 4+/** Karakalpak (Qaraqalpaqsha)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageKaa extends Language {
 9+
 10+ # Convert from the nominative form of a noun to some other case
 11+ # Invoked with {{GRAMMAR:case|word}}
 12+ /**
 13+ * Cases: genitive, dative, accusative, locative, ablative, comitative + possessive forms
 14+ *
 15+ * @param $word string
 16+ * @param $case string
 17+ *
 18+ * @return string
 19+ */
 20+ function convertGrammar( $word, $case ) {
 21+ global $wgGrammarForms;
 22+ if ( isset( $wgGrammarForms['kaa'][$case][$word] ) ) {
 23+ return $wgGrammarForms['kaa'][$case][$word];
 24+ }
 25+ /* Full code of function convertGrammar() is in development. Updates coming soon. */
 26+ return $word;
 27+ }
 28+
 29+ /**
 30+ * It fixes issue with ucfirst for transforming 'i' to 'İ'
 31+ *
 32+ * @param $string string
 33+ *
 34+ * @return string
 35+ */
 36+ function ucfirst ( $string ) {
 37+ if ( substr( $string, 0, 1 ) === 'i' ) {
 38+ return 'İ' . substr( $string, 1 );
 39+ } else {
 40+ return parent::ucfirst( $string );
 41+ }
 42+ }
 43+
 44+ /**
 45+ * It fixes issue with lcfirst for transforming 'I' to 'ı'
 46+ *
 47+ * @param $string string
 48+ *
 49+ * @return string
 50+ */
 51+ function lcfirst ( $string ) {
 52+ if ( substr( $string, 0, 1 ) === 'I' ) {
 53+ return 'ı' . substr( $string, 1 );
 54+ } else {
 55+ return parent::lcfirst( $string );
 56+ }
 57+ }
 58+
 59+ /**
 60+ * Avoid grouping whole numbers between 0 to 9999
 61+ *
 62+ * @param $_ string
 63+ *
 64+ * @return string
 65+ */
 66+ function commafy( $_ ) {
 67+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 68+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 69+ } else {
 70+ return $_;
 71+ }
 72+ }
 73+
 74+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageKaa.php
___________________________________________________________________
Added: svn:eol-style
175 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageFi.php
@@ -0,0 +1,146 @@
 2+<?php
 3+
 4+/** Finnish (Suomi)
 5+ *
 6+ * @ingroup Language
 7+ *
 8+ * @author Niklas Laxström
 9+ */
 10+class LanguageFi extends Language {
 11+
 12+ /**
 13+ * Convert from the nominative form of a noun to some other case
 14+ * Invoked with {{grammar:case|word}}
 15+ *
 16+ * @param $word string
 17+ * @param $case string
 18+ * @return string
 19+ */
 20+ function convertGrammar( $word, $case ) {
 21+ global $wgGrammarForms;
 22+ if ( isset( $wgGrammarForms['fi'][$case][$word] ) ) {
 23+ return $wgGrammarForms['fi'][$case][$word];
 24+ }
 25+
 26+ # These rules are not perfect, but they are currently only used for site names so it doesn't
 27+ # matter if they are wrong sometimes. Just add a special case for your site name if necessary.
 28+
 29+ # wovel harmony flag
 30+ $aou = preg_match( '/[aou][^äöy]*$/i', $word );
 31+
 32+ # The flag should be false for compounds where the last word has only neutral vowels (e/i).
 33+ # The general case cannot be handled without a dictionary, but there's at least one notable
 34+ # special case we should check for:
 35+
 36+ if ( preg_match( '/wiki$/i', $word ) )
 37+ $aou = false;
 38+
 39+ # append i after final consonant
 40+ if ( preg_match( '/[bcdfghjklmnpqrstvwxz]$/i', $word ) )
 41+ $word .= 'i';
 42+
 43+ switch ( $case ) {
 44+ case 'genitive':
 45+ $word .= 'n';
 46+ break;
 47+ case 'elative':
 48+ $word .= ( $aou ? 'sta' : 'stä' );
 49+ break;
 50+ case 'partitive':
 51+ $word .= ( $aou ? 'a' : 'ä' );
 52+ break;
 53+ case 'illative':
 54+ # Double the last letter and add 'n'
 55+ # mb_substr has a compatibility function in GlobalFunctions.php
 56+ $word = $word . mb_substr( $word, -1 ) . 'n';
 57+ break;
 58+ case 'inessive':
 59+ $word .= ( $aou ? 'ssa' : 'ssä' );
 60+ break;
 61+ }
 62+ return $word;
 63+ }
 64+
 65+ /**
 66+ * @param $str string
 67+ * @param $forContent bool
 68+ * @return string
 69+ */
 70+ function translateBlockExpiry( $str, $forContent = false ) {
 71+ /*
 72+ 'ago', 'now', 'today', 'this', 'next',
 73+ 'first', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth',
 74+ 'tomorrow', 'yesterday'
 75+
 76+ $months = 'january:tammikuu,february:helmikuu,march:maaliskuu,april:huhtikuu,may:toukokuu,june:kesäkuu,' .
 77+ 'july:heinäkuu,august:elokuu,september:syyskuu,october:lokakuu,november:marraskuu,december:joulukuu,' .
 78+ 'jan:tammikuu,feb:helmikuu,mar:maaliskuu,apr:huhtikuu,jun:kesäkuu,jul:heinäkuu,aug:elokuu,sep:syyskuu,'.
 79+ 'oct:lokakuu,nov:marraskuu,dec:joulukuu,sept:syyskuu';
 80+ */
 81+ $weekds = array(
 82+ 'monday' => 'maanantai',
 83+ 'tuesday' => 'tiistai',
 84+ 'wednesday' => 'keskiviikko',
 85+ 'thursay' => 'torstai',
 86+ 'friday' => 'perjantai',
 87+ 'saturday' => 'lauantai',
 88+ 'sunday' => 'sunnuntai',
 89+ 'mon' => 'ma',
 90+ 'tue' => 'ti',
 91+ 'tues' => 'ti',
 92+ 'wed' => 'ke',
 93+ 'wednes' => 'ke',
 94+ 'thu' => 'to',
 95+ 'thur' => 'to',
 96+ 'thurs' => 'to',
 97+ 'fri' => 'pe',
 98+ 'sat' => 'la',
 99+ 'sun' => 'su',
 100+ 'next' => 'seuraava',
 101+ 'tomorrow' => 'huomenna',
 102+ 'ago' => 'sitten',
 103+ 'seconds' => 'sekuntia',
 104+ 'second' => 'sekunti',
 105+ 'secs' => 's',
 106+ 'sec' => 's',
 107+ 'minutes' => 'minuuttia',
 108+ 'minute' => 'minuutti',
 109+ 'mins' => 'min',
 110+ 'min' => 'min',
 111+ 'days' => 'päivää',
 112+ 'day' => 'päivä',
 113+ 'hours' => 'tuntia',
 114+ 'hour' => 'tunti',
 115+ 'weeks' => 'viikkoa',
 116+ 'week' => 'viikko',
 117+ 'fortnights' => 'tuplaviikkoa',
 118+ 'fortnight' => 'tuplaviikko',
 119+ 'months' => 'kuukautta',
 120+ 'month' => 'kuukausi',
 121+ 'years' => 'vuotta',
 122+ 'year' => 'vuosi',
 123+ 'infinite' => 'ikuisesti',
 124+ 'indefinite' => 'ikuisesti'
 125+ );
 126+
 127+ $final = '';
 128+ $tokens = explode ( ' ', $str );
 129+ foreach ( $tokens as $item ) {
 130+ if ( !is_numeric( $item ) ) {
 131+ if ( count ( explode( '-', $item ) ) == 3 && strlen( $item ) == 10 ) {
 132+ list( $yyyy, $mm, $dd ) = explode( '-', $item );
 133+ $final .= ' ' . $this->date( "{$yyyy}{$mm}{$dd}000000" );
 134+ continue;
 135+ }
 136+ if ( isset( $weekds[$item] ) ) {
 137+ $final .= ' ' . $weekds[$item];
 138+ continue;
 139+ }
 140+ }
 141+
 142+ $final .= ' ' . $item;
 143+ }
 144+
 145+ return htmlspecialchars( trim( $final ) );
 146+ }
 147+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageFi.php
___________________________________________________________________
Added: svn:eol-style
1148 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageKk.deps.php
@@ -0,0 +1,9 @@
 2+<?php
 3+// This file exists to ensure that base classes are preloaded before
 4+// LanguageKk.php is compiled, working around a bug in the APC opcode
 5+// cache on PHP 5, where cached code can break if the include order
 6+// changed on a subsequent page view.
 7+// see http://lists.wikimedia.org/pipermail/wikitech-l/2006-January/021311.html
 8+
 9+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
 10+require_once( dirname( __FILE__ ) . '/LanguageKk_cyrl.php' );
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageKk.deps.php
___________________________________________________________________
Added: svn:eol-style
111 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageBs.php
@@ -0,0 +1,59 @@
 2+<?php
 3+
 4+/** Bosnian (bosanski)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageBs extends Language {
 9+
 10+ /**
 11+ * @param $count int
 12+ * @param $forms array
 13+ * @return string
 14+ */
 15+ function convertPlural( $count, $forms ) {
 16+ if ( !count( $forms ) ) { return ''; }
 17+ $forms = $this->preConvertPlural( $forms, 3 );
 18+
 19+ // @todo FIXME: CLDR defines 4 plural forms instead of 3. Plural for decimals is missing.
 20+ // http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 21+ if ( $count > 10 && floor( ( $count % 100 ) / 10 ) == 1 ) {
 22+ return $forms[2];
 23+ } else {
 24+ switch ( $count % 10 ) {
 25+ case 1: return $forms[0];
 26+ case 2:
 27+ case 3:
 28+ case 4: return $forms[1];
 29+ default: return $forms[2];
 30+ }
 31+ }
 32+ }
 33+
 34+ # Convert from the nominative form of a noun to some other case
 35+ # Invoked with {{GRAMMAR:case|word}}
 36+ /**
 37+ * Cases: genitiv, dativ, akuzativ, vokativ, instrumental, lokativ
 38+ *
 39+ * @param $word string
 40+ * @param $case string
 41+ *
 42+ * @return string
 43+ */
 44+ function convertGrammar( $word, $case ) {
 45+ global $wgGrammarForms;
 46+ if ( isset( $wgGrammarForms['bs'][$case][$word] ) ) {
 47+ return $wgGrammarForms['bs'][$case][$word];
 48+ }
 49+ switch ( $case ) {
 50+ case 'instrumental': # instrumental
 51+ $word = 's ' . $word;
 52+ break;
 53+ case 'lokativ': # locative
 54+ $word = 'o ' . $word;
 55+ break;
 56+ }
 57+
 58+ return $word; # this will return the original value for 'nominativ' (nominative) and all undefined case values
 59+ }
 60+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageBs.php
___________________________________________________________________
Added: svn:eol-style
161 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageGan.php
@@ -0,0 +1,146 @@
 2+<?php
 3+
 4+require_once( dirname( __FILE__ ) . '/../LanguageConverter.php' );
 5+require_once( dirname( __FILE__ ) . '/LanguageZh.php' );
 6+
 7+/**
 8+ * @ingroup Language
 9+ */
 10+class GanConverter extends LanguageConverter {
 11+
 12+ /**
 13+ * @param $langobj Language
 14+ * @param $maincode string
 15+ * @param $variants array
 16+ * @param $variantfallbacks array
 17+ * @param $flags array
 18+ * @param $manualLevel array
 19+ */
 20+ function __construct( $langobj, $maincode,
 21+ $variants = array(),
 22+ $variantfallbacks = array(),
 23+ $flags = array(),
 24+ $manualLevel = array() ) {
 25+ $this->mDescCodeSep = ':';
 26+ $this->mDescVarSep = ';';
 27+ parent::__construct( $langobj, $maincode,
 28+ $variants,
 29+ $variantfallbacks,
 30+ $flags,
 31+ $manualLevel );
 32+ $names = array(
 33+ 'gan' => '原文',
 34+ 'gan-hans' => '简体',
 35+ 'gan-hant' => '繁體',
 36+ );
 37+ $this->mVariantNames = array_merge( $this->mVariantNames, $names );
 38+ }
 39+
 40+ function loadDefaultTables() {
 41+ require( dirname( __FILE__ ) . "/../../includes/ZhConversion.php" );
 42+ $this->mTables = array(
 43+ 'gan-hans' => new ReplacementArray( $zh2Hans ),
 44+ 'gan-hant' => new ReplacementArray( $zh2Hant ),
 45+ 'gan' => new ReplacementArray
 46+ );
 47+ }
 48+
 49+ /**
 50+ * there shouldn't be any latin text in Chinese conversion, so no need
 51+ * to mark anything.
 52+ * $noParse is there for compatibility with LanguageConvert::markNoConversion
 53+ *
 54+ * @param $text string
 55+ * @param $noParse bool
 56+ *
 57+ * @return string
 58+ */
 59+ function markNoConversion( $text, $noParse = false ) {
 60+ return $text;
 61+ }
 62+
 63+ /**
 64+ * @param $key string
 65+ * @return String
 66+ */
 67+ function convertCategoryKey( $key ) {
 68+ return $this->autoConvert( $key, 'gan' );
 69+ }
 70+}
 71+
 72+/**
 73+ * class that handles both Traditional and Simplified Chinese
 74+ * right now it only distinguish gan_hans, gan_hant.
 75+ *
 76+ * @ingroup Language
 77+ */
 78+class LanguageGan extends LanguageZh {
 79+
 80+ function __construct() {
 81+
 82+ parent::__construct();
 83+
 84+ $variants = array( 'gan', 'gan-hans', 'gan-hant' );
 85+ $variantfallbacks = array(
 86+ 'gan' => array( 'gan-hans', 'gan-hant' ),
 87+ 'gan-hans' => array( 'gan' ),
 88+ 'gan-hant' => array( 'gan' ),
 89+ );
 90+ $ml = array(
 91+ 'gan' => 'disable',
 92+ );
 93+
 94+ $this->mConverter = new GanConverter( $this, 'gan',
 95+ $variants, $variantfallbacks,
 96+ array(),
 97+ $ml );
 98+
 99+ $wgHooks['ArticleSaveComplete'][] = $this->mConverter;
 100+ }
 101+
 102+ /**
 103+ * this should give much better diff info
 104+ *
 105+ * @param $text string
 106+ * @return string
 107+ */
 108+ function segmentForDiff( $text ) {
 109+ return preg_replace(
 110+ "/([\\xc0-\\xff][\\x80-\\xbf]*)/e",
 111+ "' ' .\"$1\"", $text );
 112+ }
 113+
 114+ /**
 115+ * @param $text string
 116+ * @return string
 117+ */
 118+ function unsegmentForDiff( $text ) {
 119+ return preg_replace(
 120+ "/ ([\\xc0-\\xff][\\x80-\\xbf]*)/e",
 121+ "\"$1\"", $text );
 122+ }
 123+
 124+ /**
 125+ * word segmentation
 126+ *
 127+ * @param $string string
 128+ * @param $autoVariant string
 129+ * @return String
 130+ */
 131+ function normalizeForSearch( $string, $autoVariant = 'gan-hans' ) {
 132+ // LanguageZh::normalizeForSearch
 133+ return parent::normalizeForSearch( $string, $autoVariant );
 134+ }
 135+
 136+ /**
 137+ * @param $termsArray array
 138+ * @return array
 139+ */
 140+ function convertForSearchResult( $termsArray ) {
 141+ $terms = implode( '|', $termsArray );
 142+ $terms = self::convertDoubleWidth( $terms );
 143+ $terms = implode( '|', $this->mConverter->autoConvertToAllVariants( $terms ) );
 144+ $ret = array_unique( explode( '|', $terms ) );
 145+ return $ret;
 146+ }
 147+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageGan.php
___________________________________________________________________
Added: svn:eol-style
1148 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageFr.php
@@ -0,0 +1,22 @@
 2+<?php
 3+
 4+/** French (Français)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageFr extends Language {
 9+ /**
 10+ * Use singular form for zero (see bug 7309)
 11+ *
 12+ * @param $count int
 13+ * @param $forms array
 14+ *
 15+ * @return string
 16+ */
 17+ function convertPlural( $count, $forms ) {
 18+ if ( !count( $forms ) ) { return ''; }
 19+ $forms = $this->preConvertPlural( $forms, 2 );
 20+
 21+ return ( $count <= 1 ) ? $forms[0] : $forms[1];
 22+ }
 23+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageFr.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageConverter.php
@@ -0,0 +1,1528 @@
 2+<?php
 3+/**
 4+ * Contains the LanguageConverter class and ConverterRule class
 5+ *
 6+ * This program is free software; you can redistribute it and/or modify
 7+ * it under the terms of the GNU General Public License as published by
 8+ * the Free Software Foundation; either version 2 of the License, or
 9+ * (at your option) any later version.
 10+ *
 11+ * This program is distributed in the hope that it will be useful,
 12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ * GNU General Public License for more details.
 15+ *
 16+ * You should have received a copy of the GNU General Public License along
 17+ * with this program; if not, write to the Free Software Foundation, Inc.,
 18+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 19+ * http://www.gnu.org/copyleft/gpl.html
 20+ *
 21+ * @file
 22+ * @ingroup Language
 23+ */
 24+
 25+/**
 26+ * Base class for language conversion.
 27+ * @ingroup Language
 28+ *
 29+ * @author Zhengzhu Feng <zhengzhu@gmail.com>
 30+ * @maintainers fdcn <fdcn64@gmail.com>, shinjiman <shinjiman@gmail.com>, PhiLiP <philip.npc@gmail.com>
 31+ */
 32+class LanguageConverter {
 33+ var $mMainLanguageCode;
 34+ var $mVariants, $mVariantFallbacks, $mVariantNames;
 35+ var $mTablesLoaded = false;
 36+ var $mTables;
 37+ // 'bidirectional' 'unidirectional' 'disable' for each variant
 38+ var $mManualLevel;
 39+
 40+ /**
 41+ * @var String: memcached key name
 42+ */
 43+ var $mCacheKey;
 44+
 45+ var $mLangObj;
 46+ var $mFlags;
 47+ var $mDescCodeSep = ':', $mDescVarSep = ';';
 48+ var $mUcfirst = false;
 49+ var $mConvRuleTitle = false;
 50+ var $mURLVariant;
 51+ var $mUserVariant;
 52+ var $mHeaderVariant;
 53+ var $mMaxDepth = 10;
 54+ var $mVarSeparatorPattern;
 55+
 56+ const CACHE_VERSION_KEY = 'VERSION 6';
 57+
 58+ /**
 59+ * Constructor
 60+ *
 61+ * @param $langobj Language: the Language Object
 62+ * @param $maincode String: the main language code of this language
 63+ * @param $variants Array: the supported variants of this language
 64+ * @param $variantfallbacks Array: the fallback language of each variant
 65+ * @param $flags Array: defining the custom strings that maps to the flags
 66+ * @param $manualLevel Array: limit for supported variants
 67+ */
 68+ public function __construct( $langobj, $maincode, $variants = array(),
 69+ $variantfallbacks = array(), $flags = array(),
 70+ $manualLevel = array() ) {
 71+ global $wgDisabledVariants;
 72+ $this->mLangObj = $langobj;
 73+ $this->mMainLanguageCode = $maincode;
 74+ $this->mVariants = array_diff( $variants, $wgDisabledVariants );
 75+ $this->mVariantFallbacks = $variantfallbacks;
 76+ $this->mVariantNames = Language::getLanguageNames();
 77+ $this->mCacheKey = wfMemcKey( 'conversiontables', $maincode );
 78+ $defaultflags = array(
 79+ // 'S' show converted text
 80+ // '+' add rules for alltext
 81+ // 'E' the gave flags is error
 82+ // these flags above are reserved for program
 83+ 'A' => 'A', // add rule for convert code (all text convert)
 84+ 'T' => 'T', // title convert
 85+ 'R' => 'R', // raw content
 86+ 'D' => 'D', // convert description (subclass implement)
 87+ '-' => '-', // remove convert (not implement)
 88+ 'H' => 'H', // add rule for convert code
 89+ // (but no display in placed code)
 90+ 'N' => 'N' // current variant name
 91+ );
 92+ $this->mFlags = array_merge( $defaultflags, $flags );
 93+ foreach ( $this->mVariants as $v ) {
 94+ if ( array_key_exists( $v, $manualLevel ) ) {
 95+ $this->mManualLevel[$v] = $manualLevel[$v];
 96+ } else {
 97+ $this->mManualLevel[$v] = 'bidirectional';
 98+ }
 99+ $this->mFlags[$v] = $v;
 100+ }
 101+ }
 102+
 103+ /**
 104+ * Get all valid variants.
 105+ * Call this instead of using $this->mVariants directly.
 106+ *
 107+ * @return Array: contains all valid variants
 108+ */
 109+ public function getVariants() {
 110+ return $this->mVariants;
 111+ }
 112+
 113+ /**
 114+ * In case some variant is not defined in the markup, we need
 115+ * to have some fallback. For example, in zh, normally people
 116+ * will define zh-hans and zh-hant, but less so for zh-sg or zh-hk.
 117+ * when zh-sg is preferred but not defined, we will pick zh-hans
 118+ * in this case. Right now this is only used by zh.
 119+ *
 120+ * @param $variant String: the language code of the variant
 121+ * @return String: The code of the fallback language or the
 122+ * main code if there is no fallback
 123+ */
 124+ public function getVariantFallbacks( $variant ) {
 125+ if ( isset( $this->mVariantFallbacks[$variant] ) ) {
 126+ return $this->mVariantFallbacks[$variant];
 127+ }
 128+ return $this->mMainLanguageCode;
 129+ }
 130+
 131+ /**
 132+ * Get the title produced by the conversion rule.
 133+ * @return String: The converted title text
 134+ */
 135+ public function getConvRuleTitle() {
 136+ return $this->mConvRuleTitle;
 137+ }
 138+
 139+ /**
 140+ * Get preferred language variant.
 141+ * @return String: the preferred language code
 142+ */
 143+ public function getPreferredVariant() {
 144+ global $wgDefaultLanguageVariant, $wgUser;
 145+
 146+ $req = $this->getURLVariant();
 147+
 148+ if ( $wgUser->isLoggedIn() && !$req ) {
 149+ $req = $this->getUserVariant();
 150+ } elseif ( !$req ) {
 151+ $req = $this->getHeaderVariant();
 152+ }
 153+
 154+ if ( $wgDefaultLanguageVariant && !$req ) {
 155+ $req = $this->validateVariant( $wgDefaultLanguageVariant );
 156+ }
 157+
 158+ // This function, unlike the other get*Variant functions, is
 159+ // not memoized (i.e. there return value is not cached) since
 160+ // new information might appear during processing after this
 161+ // is first called.
 162+ if ( $req ) {
 163+ return $req;
 164+ }
 165+ return $this->mMainLanguageCode;
 166+ }
 167+
 168+ /**
 169+ * Get default variant.
 170+ * This function would not be affected by user's settings or headers
 171+ * @return String: the default variant code
 172+ */
 173+ public function getDefaultVariant() {
 174+ global $wgDefaultLanguageVariant;
 175+
 176+ $req = $this->getURLVariant();
 177+
 178+ if ( $wgDefaultLanguageVariant && !$req ) {
 179+ $req = $this->validateVariant( $wgDefaultLanguageVariant );
 180+ }
 181+
 182+ if ( $req ) {
 183+ return $req;
 184+ }
 185+ return $this->mMainLanguageCode;
 186+ }
 187+
 188+ /**
 189+ * Validate the variant
 190+ * @param $variant String: the variant to validate
 191+ * @return Mixed: returns the variant if it is valid, null otherwise
 192+ */
 193+ protected function validateVariant( $variant = null ) {
 194+ if ( $variant !== null && in_array( $variant, $this->mVariants ) ) {
 195+ return $variant;
 196+ }
 197+ return null;
 198+ }
 199+
 200+ /**
 201+ * Get the variant specified in the URL
 202+ *
 203+ * @return Mixed: variant if one found, false otherwise.
 204+ */
 205+ public function getURLVariant() {
 206+ global $wgRequest;
 207+
 208+ if ( $this->mURLVariant ) {
 209+ return $this->mURLVariant;
 210+ }
 211+
 212+ // see if the preference is set in the request
 213+ $ret = $wgRequest->getText( 'variant' );
 214+
 215+ if ( !$ret ) {
 216+ $ret = $wgRequest->getVal( 'uselang' );
 217+ }
 218+
 219+ return $this->mURLVariant = $this->validateVariant( $ret );
 220+ }
 221+
 222+ /**
 223+ * Determine if the user has a variant set.
 224+ *
 225+ * @return Mixed: variant if one found, false otherwise.
 226+ */
 227+ protected function getUserVariant() {
 228+ global $wgUser;
 229+
 230+ // memoizing this function wreaks havoc on parserTest.php
 231+ /*
 232+ if ( $this->mUserVariant ) {
 233+ return $this->mUserVariant;
 234+ }
 235+ */
 236+
 237+ // Get language variant preference from logged in users
 238+ // Don't call this on stub objects because that causes infinite
 239+ // recursion during initialisation
 240+ if ( $wgUser->isLoggedIn() ) {
 241+ $ret = $wgUser->getOption( 'variant' );
 242+ } else {
 243+ // figure out user lang without constructing wgLang to avoid
 244+ // infinite recursion
 245+ $ret = $wgUser->getOption( 'language' );
 246+ }
 247+
 248+ return $this->mUserVariant = $this->validateVariant( $ret );
 249+ }
 250+
 251+ /**
 252+ * Determine the language variant from the Accept-Language header.
 253+ *
 254+ * @return Mixed: variant if one found, false otherwise.
 255+ */
 256+ protected function getHeaderVariant() {
 257+ global $wgRequest;
 258+
 259+ if ( $this->mHeaderVariant ) {
 260+ return $this->mHeaderVariant;
 261+ }
 262+
 263+ // see if some supported language variant is set in the
 264+ // HTTP header.
 265+ $languages = array_keys( $wgRequest->getAcceptLang() );
 266+ if ( empty( $languages ) ) {
 267+ return null;
 268+ }
 269+
 270+ $fallbackLanguages = array();
 271+ foreach ( $languages as $language ) {
 272+ $this->mHeaderVariant = $this->validateVariant( $language );
 273+ if ( $this->mHeaderVariant ) {
 274+ break;
 275+ }
 276+
 277+ // To see if there are fallbacks of current language.
 278+ // We record these fallback variants, and process
 279+ // them later.
 280+ $fallbacks = $this->getVariantFallbacks( $language );
 281+ if ( is_string( $fallbacks ) ) {
 282+ $fallbackLanguages[] = $fallbacks;
 283+ } elseif ( is_array( $fallbacks ) ) {
 284+ $fallbackLanguages =
 285+ array_merge( $fallbackLanguages, $fallbacks );
 286+ }
 287+ }
 288+
 289+ if ( !$this->mHeaderVariant ) {
 290+ // process fallback languages now
 291+ $fallback_languages = array_unique( $fallbackLanguages );
 292+ foreach ( $fallback_languages as $language ) {
 293+ $this->mHeaderVariant = $this->validateVariant( $language );
 294+ if ( $this->mHeaderVariant ) {
 295+ break;
 296+ }
 297+ }
 298+ }
 299+
 300+ return $this->mHeaderVariant;
 301+ }
 302+
 303+ /**
 304+ * Dictionary-based conversion.
 305+ * This function would not parse the conversion rules.
 306+ * If you want to parse rules, try to use convert() or
 307+ * convertTo().
 308+ *
 309+ * @param $text String the text to be converted
 310+ * @param $toVariant bool|string the target language code
 311+ * @return String the converted text
 312+ */
 313+ public function autoConvert( $text, $toVariant = false ) {
 314+ wfProfileIn( __METHOD__ );
 315+
 316+ $this->loadTables();
 317+
 318+ if ( !$toVariant ) {
 319+ $toVariant = $this->getPreferredVariant();
 320+ if ( !$toVariant ) {
 321+ wfProfileOut( __METHOD__ );
 322+ return $text;
 323+ }
 324+ }
 325+
 326+ if( $this->guessVariant( $text, $toVariant ) ) {
 327+ wfProfileOut( __METHOD__ );
 328+ return $text;
 329+ }
 330+
 331+ /* we convert everything except:
 332+ 1. HTML markups (anything between < and >)
 333+ 2. HTML entities
 334+ 3. placeholders created by the parser
 335+ */
 336+ global $wgParser;
 337+ if ( isset( $wgParser ) && $wgParser->UniqPrefix() != '' ) {
 338+ $marker = '|' . $wgParser->UniqPrefix() . '[\-a-zA-Z0-9]+';
 339+ } else {
 340+ $marker = '';
 341+ }
 342+
 343+ // this one is needed when the text is inside an HTML markup
 344+ $htmlfix = '|<[^>]+$|^[^<>]*>';
 345+
 346+ // disable convert to variants between <code></code> tags
 347+ $codefix = '<code>.+?<\/code>|';
 348+ // disable convertsion of <script type="text/javascript"> ... </script>
 349+ $scriptfix = '<script.*?>.*?<\/script>|';
 350+ // disable conversion of <pre xxxx> ... </pre>
 351+ $prefix = '<pre.*?>.*?<\/pre>|';
 352+
 353+ $reg = '/' . $codefix . $scriptfix . $prefix .
 354+ '<[^>]+>|&[a-zA-Z#][a-z0-9]+;' . $marker . $htmlfix . '/s';
 355+ $startPos = 0;
 356+ $sourceBlob = '';
 357+ $literalBlob = '';
 358+
 359+ // Guard against delimiter nulls in the input
 360+ $text = str_replace( "\000", '', $text );
 361+
 362+ $markupMatches = null;
 363+ $elementMatches = null;
 364+ while ( $startPos < strlen( $text ) ) {
 365+ if ( preg_match( $reg, $text, $markupMatches, PREG_OFFSET_CAPTURE, $startPos ) ) {
 366+ $elementPos = $markupMatches[0][1];
 367+ $element = $markupMatches[0][0];
 368+ } else {
 369+ $elementPos = strlen( $text );
 370+ $element = '';
 371+ }
 372+
 373+ // Queue the part before the markup for translation in a batch
 374+ $sourceBlob .= substr( $text, $startPos, $elementPos - $startPos ) . "\000";
 375+
 376+ // Advance to the next position
 377+ $startPos = $elementPos + strlen( $element );
 378+
 379+ // Translate any alt or title attributes inside the matched element
 380+ if ( $element !== '' && preg_match( '/^(<[^>\s]*)\s([^>]*)(.*)$/', $element,
 381+ $elementMatches ) )
 382+ {
 383+ $attrs = Sanitizer::decodeTagAttributes( $elementMatches[2] );
 384+ $changed = false;
 385+ foreach ( array( 'title', 'alt' ) as $attrName ) {
 386+ if ( !isset( $attrs[$attrName] ) ) {
 387+ continue;
 388+ }
 389+ $attr = $attrs[$attrName];
 390+ // Don't convert URLs
 391+ if ( !strpos( $attr, '://' ) ) {
 392+ $attr = $this->translate( $attr, $toVariant );
 393+ }
 394+
 395+ // Remove HTML tags to avoid disrupting the layout
 396+ $attr = preg_replace( '/<[^>]+>/', '', $attr );
 397+ if ( $attr !== $attrs[$attrName] ) {
 398+ $attrs[$attrName] = $attr;
 399+ $changed = true;
 400+ }
 401+ }
 402+ if ( $changed ) {
 403+ $element = $elementMatches[1] . Html::expandAttributes( $attrs ) .
 404+ $elementMatches[3];
 405+ }
 406+ }
 407+ $literalBlob .= $element . "\000";
 408+ }
 409+
 410+ // Do the main translation batch
 411+ $translatedBlob = $this->translate( $sourceBlob, $toVariant );
 412+
 413+ // Put the output back together
 414+ $translatedIter = StringUtils::explode( "\000", $translatedBlob );
 415+ $literalIter = StringUtils::explode( "\000", $literalBlob );
 416+ $output = '';
 417+ while ( $translatedIter->valid() && $literalIter->valid() ) {
 418+ $output .= $translatedIter->current();
 419+ $output .= $literalIter->current();
 420+ $translatedIter->next();
 421+ $literalIter->next();
 422+ }
 423+
 424+ wfProfileOut( __METHOD__ );
 425+ return $output;
 426+ }
 427+
 428+ /**
 429+ * Translate a string to a variant.
 430+ * Doesn't parse rules or do any of that other stuff, for that use
 431+ * convert() or convertTo().
 432+ *
 433+ * @param $text String: text to convert
 434+ * @param $variant String: variant language code
 435+ * @return String: translated text
 436+ */
 437+ public function translate( $text, $variant ) {
 438+ wfProfileIn( __METHOD__ );
 439+ // If $text is empty or only includes spaces, do nothing
 440+ // Otherwise translate it
 441+ if ( trim( $text ) ) {
 442+ $this->loadTables();
 443+ $text = $this->mTables[$variant]->replace( $text );
 444+ }
 445+ wfProfileOut( __METHOD__ );
 446+ return $text;
 447+ }
 448+
 449+ /**
 450+ * Call translate() to convert text to all valid variants.
 451+ *
 452+ * @param $text String: the text to be converted
 453+ * @return Array: variant => converted text
 454+ */
 455+ public function autoConvertToAllVariants( $text ) {
 456+ wfProfileIn( __METHOD__ );
 457+ $this->loadTables();
 458+
 459+ $ret = array();
 460+ foreach ( $this->mVariants as $variant ) {
 461+ $ret[$variant] = $this->translate( $text, $variant );
 462+ }
 463+
 464+ wfProfileOut( __METHOD__ );
 465+ return $ret;
 466+ }
 467+
 468+ /**
 469+ * Convert link text to all valid variants.
 470+ * In the first, this function only convert text outside the
 471+ * "-{" "}-" markups. Since the "{" and "}" are not allowed in
 472+ * titles, the text will get all converted always.
 473+ * So I removed this feature and deprecated the function.
 474+ *
 475+ * @param $text String: the text to be converted
 476+ * @return Array: variant => converted text
 477+ * @deprecated since 1.17 Use autoConvertToAllVariants() instead
 478+ */
 479+ public function convertLinkToAllVariants( $text ) {
 480+ return $this->autoConvertToAllVariants( $text );
 481+ }
 482+
 483+ /**
 484+ * Apply manual conversion rules.
 485+ *
 486+ * @param $convRule ConverterRule Object of ConverterRule
 487+ */
 488+ protected function applyManualConv( $convRule ) {
 489+ // Use syntax -{T|zh-cn:TitleCN; zh-tw:TitleTw}- to custom
 490+ // title conversion.
 491+ // Bug 24072: $mConvRuleTitle was overwritten by other manual
 492+ // rule(s) not for title, this breaks the title conversion.
 493+ $newConvRuleTitle = $convRule->getTitle();
 494+ if ( $newConvRuleTitle ) {
 495+ // So I add an empty check for getTitle()
 496+ $this->mConvRuleTitle = $newConvRuleTitle;
 497+ }
 498+
 499+ // merge/remove manual conversion rules to/from global table
 500+ $convTable = $convRule->getConvTable();
 501+ $action = $convRule->getRulesAction();
 502+ foreach ( $convTable as $variant => $pair ) {
 503+ if ( !$this->validateVariant( $variant ) ) {
 504+ continue;
 505+ }
 506+
 507+ if ( $action == 'add' ) {
 508+ foreach ( $pair as $from => $to ) {
 509+ // to ensure that $from and $to not be left blank
 510+ // so $this->translate() could always return a string
 511+ if ( $from || $to ) {
 512+ // more efficient than array_merge(), about 2.5 times.
 513+ $this->mTables[$variant]->setPair( $from, $to );
 514+ }
 515+ }
 516+ } elseif ( $action == 'remove' ) {
 517+ $this->mTables[$variant]->removeArray( $pair );
 518+ }
 519+ }
 520+ }
 521+
 522+ /**
 523+ * Auto convert a Title object to a readable string in the
 524+ * preferred variant.
 525+ *
 526+ * @param $title Title a object of Title
 527+ * @return String: converted title text
 528+ */
 529+ public function convertTitle( $title ) {
 530+ $variant = $this->getPreferredVariant();
 531+ $index = $title->getNamespace();
 532+ if ( $index === NS_MAIN ) {
 533+ $text = '';
 534+ } else {
 535+ // first let's check if a message has given us a converted name
 536+ $nsConvMsg = wfMessage( 'conversion-ns' . $index )->inContentLanguage();
 537+ if ( $nsConvMsg->exists() ) {
 538+ $text = $nsConvMsg->plain();
 539+ } else {
 540+ // the message does not exist, try retrieve it from the current
 541+ // variant's namespace names.
 542+ $langObj = $this->mLangObj->factory( $variant );
 543+ $text = $langObj->getFormattedNsText( $index );
 544+ }
 545+ $text .= ':';
 546+ }
 547+ $text .= $title->getText();
 548+ $text = $this->translate( $text, $variant );
 549+ return $text;
 550+ }
 551+
 552+ /**
 553+ * Convert text to different variants of a language. The automatic
 554+ * conversion is done in autoConvert(). Here we parse the text
 555+ * marked with -{}-, which specifies special conversions of the
 556+ * text that can not be accomplished in autoConvert().
 557+ *
 558+ * Syntax of the markup:
 559+ * -{code1:text1;code2:text2;...}- or
 560+ * -{flags|code1:text1;code2:text2;...}- or
 561+ * -{text}- in which case no conversion should take place for text
 562+ *
 563+ * @param $text String: text to be converted
 564+ * @return String: converted text
 565+ */
 566+ public function convert( $text ) {
 567+ $variant = $this->getPreferredVariant();
 568+ return $this->convertTo( $text, $variant );
 569+ }
 570+
 571+ /**
 572+ * Same as convert() except a extra parameter to custom variant.
 573+ *
 574+ * @param $text String: text to be converted
 575+ * @param $variant String: the target variant code
 576+ * @return String: converted text
 577+ */
 578+ public function convertTo( $text, $variant ) {
 579+ global $wgDisableLangConversion;
 580+ if ( $wgDisableLangConversion || $this->guessVariant( $text, $variant ) ) {
 581+ return $text;
 582+ }
 583+ return $this->recursiveConvertTopLevel( $text, $variant );
 584+ }
 585+
 586+ /**
 587+ * Recursively convert text on the outside. Allow to use nested
 588+ * markups to custom rules.
 589+ *
 590+ * @param $text String: text to be converted
 591+ * @param $variant String: the target variant code
 592+ * @param $depth Integer: depth of recursion
 593+ * @return String: converted text
 594+ */
 595+ protected function recursiveConvertTopLevel( $text, $variant, $depth = 0 ) {
 596+ $startPos = 0;
 597+ $out = '';
 598+ $length = strlen( $text );
 599+ while ( $startPos < $length ) {
 600+ $pos = strpos( $text, '-{', $startPos );
 601+
 602+ if ( $pos === false ) {
 603+ // No more markup, append final segment
 604+ $out .= $this->autoConvert( substr( $text, $startPos ), $variant );
 605+ return $out;
 606+ }
 607+
 608+ // Markup found
 609+ // Append initial segment
 610+ $out .= $this->autoConvert( substr( $text, $startPos, $pos - $startPos ), $variant );
 611+
 612+ // Advance position
 613+ $startPos = $pos;
 614+
 615+ // Do recursive conversion
 616+ $out .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
 617+ }
 618+
 619+ return $out;
 620+ }
 621+
 622+ /**
 623+ * Recursively convert text on the inside.
 624+ *
 625+ * @param $text String: text to be converted
 626+ * @param $variant String: the target variant code
 627+ * @param $startPos int
 628+ * @param $depth Integer: depth of recursion
 629+ *
 630+ * @return String: converted text
 631+ */
 632+ protected function recursiveConvertRule( $text, $variant, &$startPos, $depth = 0 ) {
 633+ // Quick sanity check (no function calls)
 634+ if ( $text[$startPos] !== '-' || $text[$startPos + 1] !== '{' ) {
 635+ throw new MWException( __METHOD__ . ': invalid input string' );
 636+ }
 637+
 638+ $startPos += 2;
 639+ $inner = '';
 640+ $warningDone = false;
 641+ $length = strlen( $text );
 642+
 643+ while ( $startPos < $length ) {
 644+ $m = false;
 645+ preg_match( '/-\{|\}-/', $text, $m, PREG_OFFSET_CAPTURE, $startPos );
 646+ if ( !$m ) {
 647+ // Unclosed rule
 648+ break;
 649+ }
 650+
 651+ $token = $m[0][0];
 652+ $pos = $m[0][1];
 653+
 654+ // Markup found
 655+ // Append initial segment
 656+ $inner .= substr( $text, $startPos, $pos - $startPos );
 657+
 658+ // Advance position
 659+ $startPos = $pos;
 660+
 661+ switch ( $token ) {
 662+ case '-{':
 663+ // Check max depth
 664+ if ( $depth >= $this->mMaxDepth ) {
 665+ $inner .= '-{';
 666+ if ( !$warningDone ) {
 667+ $inner .= '<span class="error">' .
 668+ wfMsgForContent( 'language-converter-depth-warning',
 669+ $this->mMaxDepth ) .
 670+ '</span>';
 671+ $warningDone = true;
 672+ }
 673+ $startPos += 2;
 674+ continue;
 675+ }
 676+ // Recursively parse another rule
 677+ $inner .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
 678+ break;
 679+ case '}-':
 680+ // Apply the rule
 681+ $startPos += 2;
 682+ $rule = new ConverterRule( $inner, $this );
 683+ $rule->parse( $variant );
 684+ $this->applyManualConv( $rule );
 685+ return $rule->getDisplay();
 686+ default:
 687+ throw new MWException( __METHOD__ . ': invalid regex match' );
 688+ }
 689+ }
 690+
 691+ // Unclosed rule
 692+ if ( $startPos < $length ) {
 693+ $inner .= substr( $text, $startPos );
 694+ }
 695+ $startPos = $length;
 696+ return '-{' . $this->autoConvert( $inner, $variant );
 697+ }
 698+
 699+ /**
 700+ * If a language supports multiple variants, it is possible that
 701+ * non-existing link in one variant actually exists in another variant.
 702+ * This function tries to find it. See e.g. LanguageZh.php
 703+ *
 704+ * @param $link String: the name of the link
 705+ * @param $nt Mixed: the title object of the link
 706+ * @param $ignoreOtherCond Boolean: to disable other conditions when
 707+ * we need to transclude a template or update a category's link
 708+ * @return Null, the input parameters may be modified upon return
 709+ */
 710+ public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
 711+ # If the article has already existed, there is no need to
 712+ # check it again, otherwise it may cause a fault.
 713+ if ( is_object( $nt ) && $nt->exists() ) {
 714+ return;
 715+ }
 716+
 717+ global $wgDisableLangConversion, $wgDisableTitleConversion, $wgRequest,
 718+ $wgUser;
 719+ $isredir = $wgRequest->getText( 'redirect', 'yes' );
 720+ $action = $wgRequest->getText( 'action' );
 721+ $linkconvert = $wgRequest->getText( 'linkconvert', 'yes' );
 722+ $disableLinkConversion = $wgDisableLangConversion
 723+ || $wgDisableTitleConversion;
 724+ $linkBatch = new LinkBatch();
 725+
 726+ $ns = NS_MAIN;
 727+
 728+ if ( $disableLinkConversion ||
 729+ ( !$ignoreOtherCond &&
 730+ ( $isredir == 'no'
 731+ || $action == 'edit'
 732+ || $action == 'submit'
 733+ || $linkconvert == 'no'
 734+ || $wgUser->getOption( 'noconvertlink' ) == 1 ) ) ) {
 735+ return;
 736+ }
 737+
 738+ if ( is_object( $nt ) ) {
 739+ $ns = $nt->getNamespace();
 740+ }
 741+
 742+ $variants = $this->autoConvertToAllVariants( $link );
 743+ if ( !$variants ) { // give up
 744+ return;
 745+ }
 746+
 747+ $titles = array();
 748+
 749+ foreach ( $variants as $v ) {
 750+ if ( $v != $link ) {
 751+ $varnt = Title::newFromText( $v, $ns );
 752+ if ( !is_null( $varnt ) ) {
 753+ $linkBatch->addObj( $varnt );
 754+ $titles[] = $varnt;
 755+ }
 756+ }
 757+ }
 758+
 759+ // fetch all variants in single query
 760+ $linkBatch->execute();
 761+
 762+ foreach ( $titles as $varnt ) {
 763+ if ( $varnt->getArticleID() > 0 ) {
 764+ $nt = $varnt;
 765+ $link = $varnt->getText();
 766+ break;
 767+ }
 768+ }
 769+ }
 770+
 771+ /**
 772+ * Returns language specific hash options.
 773+ *
 774+ * @return string
 775+ */
 776+ public function getExtraHashOptions() {
 777+ $variant = $this->getPreferredVariant();
 778+ return '!' . $variant;
 779+ }
 780+
 781+ /**
 782+ * Guess if a text is written in a variant. This should be implemented in subclasses.
 783+ *
 784+ * @param string $text the text to be checked
 785+ * @param string $variant language code of the variant to be checked for
 786+ * @return bool true if $text appears to be written in $variant, false if not
 787+ *
 788+ * @author Nikola Smolenski <smolensk@eunet.rs>
 789+ * @since 1.18
 790+ */
 791+ public function guessVariant($text, $variant) {
 792+ return false;
 793+ }
 794+
 795+ /**
 796+ * Load default conversion tables.
 797+ * This method must be implemented in derived class.
 798+ *
 799+ * @private
 800+ */
 801+ function loadDefaultTables() {
 802+ $name = get_class( $this );
 803+ throw new MWException( "Must implement loadDefaultTables() method in class $name" );
 804+ }
 805+
 806+ /**
 807+ * Load conversion tables either from the cache or the disk.
 808+ * @private
 809+ * @param $fromCache Boolean: load from memcached? Defaults to true.
 810+ */
 811+ function loadTables( $fromCache = true ) {
 812+ if ( $this->mTablesLoaded ) {
 813+ return;
 814+ }
 815+ global $wgMemc;
 816+ wfProfileIn( __METHOD__ );
 817+ $this->mTablesLoaded = true;
 818+ $this->mTables = false;
 819+ if ( $fromCache ) {
 820+ wfProfileIn( __METHOD__ . '-cache' );
 821+ $this->mTables = $wgMemc->get( $this->mCacheKey );
 822+ wfProfileOut( __METHOD__ . '-cache' );
 823+ }
 824+ if ( !$this->mTables
 825+ || !array_key_exists( self::CACHE_VERSION_KEY, $this->mTables ) ) {
 826+ wfProfileIn( __METHOD__ . '-recache' );
 827+ // not in cache, or we need a fresh reload.
 828+ // We will first load the default tables
 829+ // then update them using things in MediaWiki:Conversiontable/*
 830+ $this->loadDefaultTables();
 831+ foreach ( $this->mVariants as $var ) {
 832+ $cached = $this->parseCachedTable( $var );
 833+ $this->mTables[$var]->mergeArray( $cached );
 834+ }
 835+
 836+ $this->postLoadTables();
 837+ $this->mTables[self::CACHE_VERSION_KEY] = true;
 838+
 839+ $wgMemc->set( $this->mCacheKey, $this->mTables, 43200 );
 840+ wfProfileOut( __METHOD__ . '-recache' );
 841+ }
 842+ wfProfileOut( __METHOD__ );
 843+ }
 844+
 845+ /**
 846+ * Hook for post processing after conversion tables are loaded.
 847+ */
 848+ function postLoadTables() { }
 849+
 850+ /**
 851+ * Reload the conversion tables.
 852+ *
 853+ * @private
 854+ */
 855+ function reloadTables() {
 856+ if ( $this->mTables ) {
 857+ unset( $this->mTables );
 858+ }
 859+ $this->mTablesLoaded = false;
 860+ $this->loadTables( false );
 861+ }
 862+
 863+ /**
 864+ * Parse the conversion table stored in the cache.
 865+ *
 866+ * The tables should be in blocks of the following form:
 867+ * -{
 868+ * word => word ;
 869+ * word => word ;
 870+ * ...
 871+ * }-
 872+ *
 873+ * To make the tables more manageable, subpages are allowed
 874+ * and will be parsed recursively if $recursive == true.
 875+ *
 876+ * @param $code String: language code
 877+ * @param $subpage String: subpage name
 878+ * @param $recursive Boolean: parse subpages recursively? Defaults to true.
 879+ *
 880+ * @return array
 881+ */
 882+ function parseCachedTable( $code, $subpage = '', $recursive = true ) {
 883+ static $parsed = array();
 884+
 885+ $key = 'Conversiontable/' . $code;
 886+ if ( $subpage ) {
 887+ $key .= '/' . $subpage;
 888+ }
 889+ if ( array_key_exists( $key, $parsed ) ) {
 890+ return array();
 891+ }
 892+
 893+ if ( strpos( $code, '/' ) === false ) {
 894+ $txt = MessageCache::singleton()->get( 'Conversiontable', true, $code );
 895+ if ( $txt === false ) {
 896+ # @todo FIXME: This method doesn't seem to be expecting
 897+ # this possible outcome...
 898+ $txt = '&lt;Conversiontable&gt;';
 899+ }
 900+ } else {
 901+ $title = Title::makeTitleSafe(
 902+ NS_MEDIAWIKI,
 903+ "Conversiontable/$code"
 904+ );
 905+ if ( $title && $title->exists() ) {
 906+ $article = new Article( $title );
 907+ $txt = $article->getContents();
 908+ } else {
 909+ $txt = '';
 910+ }
 911+ }
 912+
 913+ // get all subpage links of the form
 914+ // [[MediaWiki:Conversiontable/zh-xx/...|...]]
 915+ $linkhead = $this->mLangObj->getNsText( NS_MEDIAWIKI ) .
 916+ ':Conversiontable';
 917+ $subs = StringUtils::explode( '[[', $txt );
 918+ $sublinks = array();
 919+ foreach ( $subs as $sub ) {
 920+ $link = explode( ']]', $sub, 2 );
 921+ if ( count( $link ) != 2 ) {
 922+ continue;
 923+ }
 924+ $b = explode( '|', $link[0], 2 );
 925+ $b = explode( '/', trim( $b[0] ), 3 );
 926+ if ( count( $b ) == 3 ) {
 927+ $sublink = $b[2];
 928+ } else {
 929+ $sublink = '';
 930+ }
 931+
 932+ if ( $b[0] == $linkhead && $b[1] == $code ) {
 933+ $sublinks[] = $sublink;
 934+ }
 935+ }
 936+
 937+ // parse the mappings in this page
 938+ $blocks = StringUtils::explode( '-{', $txt );
 939+ $ret = array();
 940+ $first = true;
 941+ foreach ( $blocks as $block ) {
 942+ if ( $first ) {
 943+ // Skip the part before the first -{
 944+ $first = false;
 945+ continue;
 946+ }
 947+ $mappings = explode( '}-', $block, 2 );
 948+ $stripped = str_replace( array( "'", '"', '*', '#' ), '',
 949+ $mappings[0] );
 950+ $table = StringUtils::explode( ';', $stripped );
 951+ foreach ( $table as $t ) {
 952+ $m = explode( '=>', $t, 3 );
 953+ if ( count( $m ) != 2 ) {
 954+ continue;
 955+ }
 956+ // trim any trailling comments starting with '//'
 957+ $tt = explode( '//', $m[1], 2 );
 958+ $ret[trim( $m[0] )] = trim( $tt[0] );
 959+ }
 960+ }
 961+ $parsed[$key] = true;
 962+
 963+ // recursively parse the subpages
 964+ if ( $recursive ) {
 965+ foreach ( $sublinks as $link ) {
 966+ $s = $this->parseCachedTable( $code, $link, $recursive );
 967+ $ret = array_merge( $ret, $s );
 968+ }
 969+ }
 970+
 971+ if ( $this->mUcfirst ) {
 972+ foreach ( $ret as $k => $v ) {
 973+ $ret[$this->mLangObj->ucfirst( $k )] = $this->mLangObj->ucfirst( $v );
 974+ }
 975+ }
 976+ return $ret;
 977+ }
 978+
 979+ /**
 980+ * Enclose a string with the "no conversion" tag. This is used by
 981+ * various functions in the Parser.
 982+ *
 983+ * @param $text String: text to be tagged for no conversion
 984+ * @param $noParse Boolean: unused
 985+ * @return String: the tagged text
 986+ */
 987+ public function markNoConversion( $text, $noParse = false ) {
 988+ # don't mark if already marked
 989+ if ( strpos( $text, '-{' ) || strpos( $text, '}-' ) ) {
 990+ return $text;
 991+ }
 992+
 993+ $ret = "-{R|$text}-";
 994+ return $ret;
 995+ }
 996+
 997+ /**
 998+ * Convert the sorting key for category links. This should make different
 999+ * keys that are variants of each other map to the same key.
 1000+ *
 1001+ * @param $key string
 1002+ *
 1003+ * @return string
 1004+ */
 1005+ function convertCategoryKey( $key ) {
 1006+ return $key;
 1007+ }
 1008+
 1009+ /**
 1010+ * Hook to refresh the cache of conversion tables when
 1011+ * MediaWiki:Conversiontable* is updated.
 1012+ * @private
 1013+ *
 1014+ * @param $article Article object
 1015+ * @param $user Object: User object for the current user
 1016+ * @param $text String: article text (?)
 1017+ * @param $summary String: edit summary of the edit
 1018+ * @param $isMinor Boolean: was the edit marked as minor?
 1019+ * @param $isWatch Boolean: did the user watch this page or not?
 1020+ * @param $section Unused
 1021+ * @param $flags Bitfield
 1022+ * @param $revision Object: new Revision object or null
 1023+ * @return Boolean: true
 1024+ */
 1025+ function OnArticleSaveComplete( $article, $user, $text, $summary, $isMinor,
 1026+ $isWatch, $section, $flags, $revision ) {
 1027+ $titleobj = $article->getTitle();
 1028+ if ( $titleobj->getNamespace() == NS_MEDIAWIKI ) {
 1029+ $title = $titleobj->getDBkey();
 1030+ $t = explode( '/', $title, 3 );
 1031+ $c = count( $t );
 1032+ if ( $c > 1 && $t[0] == 'Conversiontable' ) {
 1033+ if ( $this->validateVariant( $t[1] ) ) {
 1034+ $this->reloadTables();
 1035+ }
 1036+ }
 1037+ }
 1038+ return true;
 1039+ }
 1040+
 1041+ /**
 1042+ * Armour rendered math against conversion.
 1043+ * Escape special chars in parsed math text. (in most cases are img elements)
 1044+ *
 1045+ * @param $text String: text to armour against conversion
 1046+ * @return String: armoured text where { and } have been converted to
 1047+ * &#123; and &#125;
 1048+ */
 1049+ public function armourMath( $text ) {
 1050+ // convert '-{' and '}-' to '-&#123;' and '&#125;-' to prevent
 1051+ // any unwanted markup appearing in the math image tag.
 1052+ $text = strtr( $text, array( '-{' => '-&#123;', '}-' => '&#125;-' ) );
 1053+ return $text;
 1054+ }
 1055+
 1056+ /**
 1057+ * Get the cached separator pattern for ConverterRule::parseRules()
 1058+ */
 1059+ function getVarSeparatorPattern() {
 1060+ if ( is_null( $this->mVarSeparatorPattern ) ) {
 1061+ // varsep_pattern for preg_split:
 1062+ // text should be splited by ";" only if a valid variant
 1063+ // name exist after the markup, for example:
 1064+ // -{zh-hans:<span style="font-size:120%;">xxx</span>;zh-hant:\
 1065+ // <span style="font-size:120%;">yyy</span>;}-
 1066+ // we should split it as:
 1067+ // array(
 1068+ // [0] => 'zh-hans:<span style="font-size:120%;">xxx</span>'
 1069+ // [1] => 'zh-hant:<span style="font-size:120%;">yyy</span>'
 1070+ // [2] => ''
 1071+ // )
 1072+ $pat = '/;\s*(?=';
 1073+ foreach ( $this->mVariants as $variant ) {
 1074+ // zh-hans:xxx;zh-hant:yyy
 1075+ $pat .= $variant . '\s*:|';
 1076+ // xxx=>zh-hans:yyy; xxx=>zh-hant:zzz
 1077+ $pat .= '[^;]*?=>\s*' . $variant . '\s*:|';
 1078+ }
 1079+ $pat .= '\s*$)/';
 1080+ $this->mVarSeparatorPattern = $pat;
 1081+ }
 1082+ return $this->mVarSeparatorPattern;
 1083+ }
 1084+}
 1085+
 1086+/**
 1087+ * Parser for rules of language conversion , parse rules in -{ }- tag.
 1088+ * @ingroup Language
 1089+ * @author fdcn <fdcn64@gmail.com>, PhiLiP <philip.npc@gmail.com>
 1090+ */
 1091+class ConverterRule {
 1092+ var $mText; // original text in -{text}-
 1093+ var $mConverter; // LanguageConverter object
 1094+ var $mManualCodeError = '<strong class="error">code error!</strong>';
 1095+ var $mRuleDisplay = '';
 1096+ var $mRuleTitle = false;
 1097+ var $mRules = '';// string : the text of the rules
 1098+ var $mRulesAction = 'none';
 1099+ var $mFlags = array();
 1100+ var $mVariantFlags = array();
 1101+ var $mConvTable = array();
 1102+ var $mBidtable = array();// array of the translation in each variant
 1103+ var $mUnidtable = array();// array of the translation in each variant
 1104+
 1105+ /**
 1106+ * Constructor
 1107+ *
 1108+ * @param $text String: the text between -{ and }-
 1109+ * @param $converter LanguageConverter object
 1110+ */
 1111+ public function __construct( $text, $converter ) {
 1112+ $this->mText = $text;
 1113+ $this->mConverter = $converter;
 1114+ }
 1115+
 1116+ /**
 1117+ * Check if variants array in convert array.
 1118+ *
 1119+ * @param $variants Array or string: variant language code
 1120+ * @return String: translated text
 1121+ */
 1122+ public function getTextInBidtable( $variants ) {
 1123+ $variants = (array)$variants;
 1124+ if ( !$variants ) {
 1125+ return false;
 1126+ }
 1127+ foreach ( $variants as $variant ) {
 1128+ if ( isset( $this->mBidtable[$variant] ) ) {
 1129+ return $this->mBidtable[$variant];
 1130+ }
 1131+ }
 1132+ return false;
 1133+ }
 1134+
 1135+ /**
 1136+ * Parse flags with syntax -{FLAG| ... }-
 1137+ * @private
 1138+ */
 1139+ function parseFlags() {
 1140+ $text = $this->mText;
 1141+ $flags = array();
 1142+ $variantFlags = array();
 1143+
 1144+ $sepPos = strpos( $text, '|' );
 1145+ if ( $sepPos !== false ) {
 1146+ $validFlags = $this->mConverter->mFlags;
 1147+ $f = StringUtils::explode( ';', substr( $text, 0, $sepPos ) );
 1148+ foreach ( $f as $ff ) {
 1149+ $ff = trim( $ff );
 1150+ if ( isset( $validFlags[$ff] ) ) {
 1151+ $flags[$validFlags[$ff]] = true;
 1152+ }
 1153+ }
 1154+ $text = strval( substr( $text, $sepPos + 1 ) );
 1155+ }
 1156+
 1157+ if ( !$flags ) {
 1158+ $flags['S'] = true;
 1159+ } elseif ( isset( $flags['R'] ) ) {
 1160+ $flags = array( 'R' => true );// remove other flags
 1161+ } elseif ( isset( $flags['N'] ) ) {
 1162+ $flags = array( 'N' => true );// remove other flags
 1163+ } elseif ( isset( $flags['-'] ) ) {
 1164+ $flags = array( '-' => true );// remove other flags
 1165+ } elseif ( count( $flags ) == 1 && isset( $flags['T'] ) ) {
 1166+ $flags['H'] = true;
 1167+ } elseif ( isset( $flags['H'] ) ) {
 1168+ // replace A flag, and remove other flags except T
 1169+ $temp = array( '+' => true, 'H' => true );
 1170+ if ( isset( $flags['T'] ) ) {
 1171+ $temp['T'] = true;
 1172+ }
 1173+ if ( isset( $flags['D'] ) ) {
 1174+ $temp['D'] = true;
 1175+ }
 1176+ $flags = $temp;
 1177+ } else {
 1178+ if ( isset( $flags['A'] ) ) {
 1179+ $flags['+'] = true;
 1180+ $flags['S'] = true;
 1181+ }
 1182+ if ( isset( $flags['D'] ) ) {
 1183+ unset( $flags['S'] );
 1184+ }
 1185+ // try to find flags like "zh-hans", "zh-hant"
 1186+ // allow syntaxes like "-{zh-hans;zh-hant|XXXX}-"
 1187+ $variantFlags = array_intersect( array_keys( $flags ), $this->mConverter->mVariants );
 1188+ if ( $variantFlags ) {
 1189+ $variantFlags = array_flip( $variantFlags );
 1190+ $flags = array();
 1191+ }
 1192+ }
 1193+ $this->mVariantFlags = $variantFlags;
 1194+ $this->mRules = $text;
 1195+ $this->mFlags = $flags;
 1196+ }
 1197+
 1198+ /**
 1199+ * Generate conversion table.
 1200+ * @private
 1201+ */
 1202+ function parseRules() {
 1203+ $rules = $this->mRules;
 1204+ $bidtable = array();
 1205+ $unidtable = array();
 1206+ $variants = $this->mConverter->mVariants;
 1207+ $varsep_pattern = $this->mConverter->getVarSeparatorPattern();
 1208+
 1209+ $choice = preg_split( $varsep_pattern, $rules );
 1210+
 1211+ foreach ( $choice as $c ) {
 1212+ $v = explode( ':', $c, 2 );
 1213+ if ( count( $v ) != 2 ) {
 1214+ // syntax error, skip
 1215+ continue;
 1216+ }
 1217+ $to = trim( $v[1] );
 1218+ $v = trim( $v[0] );
 1219+ $u = explode( '=>', $v, 2 );
 1220+ // if $to is empty, strtr() could return a wrong result
 1221+ if ( count( $u ) == 1 && $to && in_array( $v, $variants ) ) {
 1222+ $bidtable[$v] = $to;
 1223+ } elseif ( count( $u ) == 2 ) {
 1224+ $from = trim( $u[0] );
 1225+ $v = trim( $u[1] );
 1226+ if ( array_key_exists( $v, $unidtable )
 1227+ && !is_array( $unidtable[$v] )
 1228+ && $to
 1229+ && in_array( $v, $variants ) ) {
 1230+ $unidtable[$v] = array( $from => $to );
 1231+ } elseif ( $to && in_array( $v, $variants ) ) {
 1232+ $unidtable[$v][$from] = $to;
 1233+ }
 1234+ }
 1235+ // syntax error, pass
 1236+ if ( !isset( $this->mConverter->mVariantNames[$v] ) ) {
 1237+ $bidtable = array();
 1238+ $unidtable = array();
 1239+ break;
 1240+ }
 1241+ }
 1242+ $this->mBidtable = $bidtable;
 1243+ $this->mUnidtable = $unidtable;
 1244+ }
 1245+
 1246+ /**
 1247+ * @private
 1248+ *
 1249+ * @return string
 1250+ */
 1251+ function getRulesDesc() {
 1252+ $codesep = $this->mConverter->mDescCodeSep;
 1253+ $varsep = $this->mConverter->mDescVarSep;
 1254+ $text = '';
 1255+ foreach ( $this->mBidtable as $k => $v ) {
 1256+ $text .= $this->mConverter->mVariantNames[$k] . "$codesep$v$varsep";
 1257+ }
 1258+ foreach ( $this->mUnidtable as $k => $a ) {
 1259+ foreach ( $a as $from => $to ) {
 1260+ $text .= $from . '⇒' . $this->mConverter->mVariantNames[$k] .
 1261+ "$codesep$to$varsep";
 1262+ }
 1263+ }
 1264+ return $text;
 1265+ }
 1266+
 1267+ /**
 1268+ * Parse rules conversion.
 1269+ * @private
 1270+ *
 1271+ * @param $variant
 1272+ *
 1273+ * @return string
 1274+ */
 1275+ function getRuleConvertedStr( $variant ) {
 1276+ $bidtable = $this->mBidtable;
 1277+ $unidtable = $this->mUnidtable;
 1278+
 1279+ if ( count( $bidtable ) + count( $unidtable ) == 0 ) {
 1280+ return $this->mRules;
 1281+ } else {
 1282+ // display current variant in bidirectional array
 1283+ $disp = $this->getTextInBidtable( $variant );
 1284+ // or display current variant in fallbacks
 1285+ if ( !$disp ) {
 1286+ $disp = $this->getTextInBidtable(
 1287+ $this->mConverter->getVariantFallbacks( $variant ) );
 1288+ }
 1289+ // or display current variant in unidirectional array
 1290+ if ( !$disp && array_key_exists( $variant, $unidtable ) ) {
 1291+ $disp = array_values( $unidtable[$variant] );
 1292+ $disp = $disp[0];
 1293+ }
 1294+ // or display frist text under disable manual convert
 1295+ if ( !$disp
 1296+ && $this->mConverter->mManualLevel[$variant] == 'disable' ) {
 1297+ if ( count( $bidtable ) > 0 ) {
 1298+ $disp = array_values( $bidtable );
 1299+ $disp = $disp[0];
 1300+ } else {
 1301+ $disp = array_values( $unidtable );
 1302+ $disp = array_values( $disp[0] );
 1303+ $disp = $disp[0];
 1304+ }
 1305+ }
 1306+ return $disp;
 1307+ }
 1308+ }
 1309+
 1310+ /**
 1311+ * Generate conversion table for all text.
 1312+ * @private
 1313+ */
 1314+ function generateConvTable() {
 1315+ // Special case optimisation
 1316+ if ( !$this->mBidtable && !$this->mUnidtable ) {
 1317+ $this->mConvTable = array();
 1318+ return;
 1319+ }
 1320+
 1321+ $bidtable = $this->mBidtable;
 1322+ $unidtable = $this->mUnidtable;
 1323+ $manLevel = $this->mConverter->mManualLevel;
 1324+
 1325+ $vmarked = array();
 1326+ foreach ( $this->mConverter->mVariants as $v ) {
 1327+ /* for bidirectional array
 1328+ fill in the missing variants, if any,
 1329+ with fallbacks */
 1330+ if ( !isset( $bidtable[$v] ) ) {
 1331+ $variantFallbacks =
 1332+ $this->mConverter->getVariantFallbacks( $v );
 1333+ $vf = $this->getTextInBidtable( $variantFallbacks );
 1334+ if ( $vf ) {
 1335+ $bidtable[$v] = $vf;
 1336+ }
 1337+ }
 1338+
 1339+ if ( isset( $bidtable[$v] ) ) {
 1340+ foreach ( $vmarked as $vo ) {
 1341+ // use syntax: -{A|zh:WordZh;zh-tw:WordTw}-
 1342+ // or -{H|zh:WordZh;zh-tw:WordTw}-
 1343+ // or -{-|zh:WordZh;zh-tw:WordTw}-
 1344+ // to introduce a custom mapping between
 1345+ // words WordZh and WordTw in the whole text
 1346+ if ( $manLevel[$v] == 'bidirectional' ) {
 1347+ $this->mConvTable[$v][$bidtable[$vo]] = $bidtable[$v];
 1348+ }
 1349+ if ( $manLevel[$vo] == 'bidirectional' ) {
 1350+ $this->mConvTable[$vo][$bidtable[$v]] = $bidtable[$vo];
 1351+ }
 1352+ }
 1353+ $vmarked[] = $v;
 1354+ }
 1355+ /* for unidirectional array fill to convert tables */
 1356+ if ( ( $manLevel[$v] == 'bidirectional' || $manLevel[$v] == 'unidirectional' )
 1357+ && isset( $unidtable[$v] ) )
 1358+ {
 1359+ if ( isset( $this->mConvTable[$v] ) ) {
 1360+ $this->mConvTable[$v] = array_merge( $this->mConvTable[$v], $unidtable[$v] );
 1361+ } else {
 1362+ $this->mConvTable[$v] = $unidtable[$v];
 1363+ }
 1364+ }
 1365+ }
 1366+ }
 1367+
 1368+ /**
 1369+ * Parse rules and flags.
 1370+ * @param $variant String: variant language code
 1371+ */
 1372+ public function parse( $variant = null ) {
 1373+ if ( !$variant ) {
 1374+ $variant = $this->mConverter->getPreferredVariant();
 1375+ }
 1376+
 1377+ $this->parseFlags();
 1378+ $flags = $this->mFlags;
 1379+
 1380+ // convert to specified variant
 1381+ // syntax: -{zh-hans;zh-hant[;...]|<text to convert>}-
 1382+ if ( $this->mVariantFlags ) {
 1383+ // check if current variant in flags
 1384+ if ( isset( $this->mVariantFlags[$variant] ) ) {
 1385+ // then convert <text to convert> to current language
 1386+ $this->mRules = $this->mConverter->autoConvert( $this->mRules,
 1387+ $variant );
 1388+ } else { // if current variant no in flags,
 1389+ // then we check its fallback variants.
 1390+ $variantFallbacks =
 1391+ $this->mConverter->getVariantFallbacks( $variant );
 1392+ foreach ( $variantFallbacks as $variantFallback ) {
 1393+ // if current variant's fallback exist in flags
 1394+ if ( isset( $this->mVariantFlags[$variantFallback] ) ) {
 1395+ // then convert <text to convert> to fallback language
 1396+ $this->mRules =
 1397+ $this->mConverter->autoConvert( $this->mRules,
 1398+ $variantFallback );
 1399+ break;
 1400+ }
 1401+ }
 1402+ }
 1403+ $this->mFlags = $flags = array( 'R' => true );
 1404+ }
 1405+
 1406+ if ( !isset( $flags['R'] ) && !isset( $flags['N'] ) ) {
 1407+ // decode => HTML entities modified by Sanitizer::removeHTMLtags
 1408+ $this->mRules = str_replace( '=&gt;', '=>', $this->mRules );
 1409+ $this->parseRules();
 1410+ }
 1411+ $rules = $this->mRules;
 1412+
 1413+ if ( !$this->mBidtable && !$this->mUnidtable ) {
 1414+ if ( isset( $flags['+'] ) || isset( $flags['-'] ) ) {
 1415+ // fill all variants if text in -{A/H/-|text} without rules
 1416+ foreach ( $this->mConverter->mVariants as $v ) {
 1417+ $this->mBidtable[$v] = $rules;
 1418+ }
 1419+ } elseif ( !isset( $flags['N'] ) && !isset( $flags['T'] ) ) {
 1420+ $this->mFlags = $flags = array( 'R' => true );
 1421+ }
 1422+ }
 1423+
 1424+ $this->mRuleDisplay = false;
 1425+ foreach ( $flags as $flag => $unused ) {
 1426+ switch ( $flag ) {
 1427+ case 'R':
 1428+ // if we don't do content convert, still strip the -{}- tags
 1429+ $this->mRuleDisplay = $rules;
 1430+ break;
 1431+ case 'N':
 1432+ // process N flag: output current variant name
 1433+ $ruleVar = trim( $rules );
 1434+ if ( isset( $this->mConverter->mVariantNames[$ruleVar] ) ) {
 1435+ $this->mRuleDisplay = $this->mConverter->mVariantNames[$ruleVar];
 1436+ } else {
 1437+ $this->mRuleDisplay = '';
 1438+ }
 1439+ break;
 1440+ case 'D':
 1441+ // process D flag: output rules description
 1442+ $this->mRuleDisplay = $this->getRulesDesc();
 1443+ break;
 1444+ case 'H':
 1445+ // process H,- flag or T only: output nothing
 1446+ $this->mRuleDisplay = '';
 1447+ break;
 1448+ case '-':
 1449+ $this->mRulesAction = 'remove';
 1450+ $this->mRuleDisplay = '';
 1451+ break;
 1452+ case '+':
 1453+ $this->mRulesAction = 'add';
 1454+ $this->mRuleDisplay = '';
 1455+ break;
 1456+ case 'S':
 1457+ $this->mRuleDisplay = $this->getRuleConvertedStr( $variant );
 1458+ break;
 1459+ case 'T':
 1460+ $this->mRuleTitle = $this->getRuleConvertedStr( $variant );
 1461+ $this->mRuleDisplay = '';
 1462+ break;
 1463+ default:
 1464+ // ignore unknown flags (but see error case below)
 1465+ }
 1466+ }
 1467+ if ( $this->mRuleDisplay === false ) {
 1468+ $this->mRuleDisplay = $this->mManualCodeError;
 1469+ }
 1470+
 1471+ $this->generateConvTable();
 1472+ }
 1473+
 1474+ /**
 1475+ * @todo FIXME: code this function :)
 1476+ */
 1477+ public function hasRules() {
 1478+ // TODO:
 1479+ }
 1480+
 1481+ /**
 1482+ * Get display text on markup -{...}-
 1483+ * @return string
 1484+ */
 1485+ public function getDisplay() {
 1486+ return $this->mRuleDisplay;
 1487+ }
 1488+
 1489+ /**
 1490+ * Get converted title.
 1491+ * @return string
 1492+ */
 1493+ public function getTitle() {
 1494+ return $this->mRuleTitle;
 1495+ }
 1496+
 1497+ /**
 1498+ * Return how deal with conversion rules.
 1499+ * @return string
 1500+ */
 1501+ public function getRulesAction() {
 1502+ return $this->mRulesAction;
 1503+ }
 1504+
 1505+ /**
 1506+ * Get conversion table. (bidirectional and unidirectional
 1507+ * conversion table)
 1508+ * @return array
 1509+ */
 1510+ public function getConvTable() {
 1511+ return $this->mConvTable;
 1512+ }
 1513+
 1514+ /**
 1515+ * Get conversion rules string.
 1516+ * @return string
 1517+ */
 1518+ public function getRules() {
 1519+ return $this->mRules;
 1520+ }
 1521+
 1522+ /**
 1523+ * Get conversion flags.
 1524+ * @return array
 1525+ */
 1526+ public function getFlags() {
 1527+ return $this->mFlags;
 1528+ }
 1529+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageConverter.php
___________________________________________________________________
Added: svn:eol-style
11530 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageDsb.php
@@ -0,0 +1,51 @@
 2+<?php
 3+
 4+/** Lower Sorbian (Dolnoserbski)
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageDsb extends Language {
 9+
 10+ /**
 11+ * Convert from the nominative form of a noun to some other case
 12+ * Invoked with {{grammar:case|word}}
 13+ *
 14+ * @param $word string
 15+ * @param $case string
 16+ * @return string
 17+ */
 18+ function convertGrammar( $word, $case ) {
 19+ global $wgGrammarForms;
 20+ if ( isset( $wgGrammarForms['dsb'][$case][$word] ) ) {
 21+ return $wgGrammarForms['dsb'][$case][$word];
 22+ }
 23+
 24+ switch ( $case ) {
 25+ case 'instrumental': # instrumental
 26+ $word = 'z ' . $word;
 27+ case 'lokatiw': # lokatiw
 28+ $word = 'wo ' . $word;
 29+ break;
 30+ }
 31+
 32+ return $word; # this will return the original value for 'nominatiw' (nominativ) and all undefined case values
 33+ }
 34+
 35+ /**
 36+ * @param $count int
 37+ * @param $forms array
 38+ * @return string
 39+ */
 40+ function convertPlural( $count, $forms ) {
 41+ if ( !count( $forms ) ) { return ''; }
 42+ $forms = $this->preConvertPlural( $forms, 4 );
 43+
 44+ switch ( abs( $count ) % 100 ) {
 45+ case 1: return $forms[0]; // singular
 46+ case 2: return $forms[1]; // dual
 47+ case 3:
 48+ case 4: return $forms[2]; // plural
 49+ default: return $forms[3]; // pluralgen
 50+ }
 51+ }
 52+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageDsb.php
___________________________________________________________________
Added: svn:eol-style
153 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageHsb.php
@@ -0,0 +1,52 @@
 2+<?php
 3+/** Upper Sorbian (Hornjoserbsce)
 4+ *
 5+ * @ingroup Language
 6+ */
 7+
 8+class LanguageHsb extends Language {
 9+
 10+ /**
 11+ * Convert from the nominative form of a noun to some other case
 12+ * Invoked with {{grammar:case|word}}
 13+ *
 14+ * @param $word string
 15+ * @param $case string
 16+ * @return string
 17+ */
 18+ function convertGrammar( $word, $case ) {
 19+ global $wgGrammarForms;
 20+ if ( isset( $wgGrammarForms['hsb'][$case][$word] ) ) {
 21+ return $wgGrammarForms['hsb'][$case][$word];
 22+ }
 23+
 24+ switch ( $case ) {
 25+ case 'instrumental': # instrumental
 26+ $word = 'z ' . $word;
 27+ break;
 28+ case 'lokatiw': # lokatiw
 29+ $word = 'wo ' . $word;
 30+ break;
 31+ }
 32+
 33+ return $word; # this will return the original value for 'nominatiw' (nominativ) and all undefined case values
 34+ }
 35+
 36+ /**
 37+ * @param $count int
 38+ * @param $forms array
 39+ * @return string
 40+ */
 41+ function convertPlural( $count, $forms ) {
 42+ if ( !count( $forms ) ) { return ''; }
 43+ $forms = $this->preConvertPlural( $forms, 4 );
 44+
 45+ switch ( abs( $count ) % 100 ) {
 46+ case 1: return $forms[0]; // singular
 47+ case 2: return $forms[1]; // dual
 48+ case 3:
 49+ case 4: return $forms[2]; // plural
 50+ default: return $forms[3]; // pluralgen
 51+ }
 52+ }
 53+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageHsb.php
___________________________________________________________________
Added: svn:eol-style
154 + native
Index: trunk/tools/ToolserverI18N/language/classes/LanguageKu_ku.php
@@ -0,0 +1,24 @@
 2+<?php
 3+
 4+/** Kurdish
 5+ *
 6+ * @ingroup Language
 7+ */
 8+class LanguageKu_ku extends Language {
 9+
 10+ /**
 11+ * Avoid grouping whole numbers between 0 to 9999
 12+ *
 13+ * @param $_ string
 14+ *
 15+ * @return string
 16+ */
 17+ function commafy( $_ ) {
 18+
 19+ if ( !preg_match( '/^\d{1,4}$/', $_ ) ) {
 20+ return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $_ ) ) );
 21+ } else {
 22+ return $_;
 23+ }
 24+ }
 25+}
Property changes on: trunk/tools/ToolserverI18N/language/classes/LanguageKu_ku.php
___________________________________________________________________
Added: svn:eol-style
126 + native
Index: trunk/tools/ToolserverI18N/language/classes/Language.php
@@ -0,0 +1,3527 @@
 2+<?php
 3+/**
 4+ * Internationalisation code
 5+ *
 6+ * @file
 7+ * @ingroup Language
 8+ */
 9+
 10+/**
 11+ * @defgroup Language Language
 12+ */
 13+
 14+if ( !defined( 'TS_INTUITION' ) ) {
 15+ echo "This file is part of Toolserver Intuition, it is not a valid entry point.\n";
 16+ exit( 1 );
 17+}
 18+
 19+# Read language names
 20+global $wgLanguageNames;
 21+require_once( dirname( __FILE__ ) . '/Names.php' );
 22+
 23+if ( function_exists( 'mb_strtoupper' ) ) {
 24+ mb_internal_encoding( 'UTF-8' );
 25+}
 26+
 27+/**
 28+ * a fake language converter
 29+ *
 30+ * @ingroup Language
 31+ */
 32+class FakeConverter {
 33+ var $mLang;
 34+ function __construct( $langobj ) { $this->mLang = $langobj; }
 35+ function autoConvertToAllVariants( $text ) { return array( $this->mLang->getCode() => $text ); }
 36+ function convert( $t ) { return $t; }
 37+ function convertTitle( $t ) { return $t->getPrefixedText(); }
 38+ function getVariants() { return array( $this->mLang->getCode() ); }
 39+ function getPreferredVariant() { return $this->mLang->getCode(); }
 40+ function getDefaultVariant() { return $this->mLang->getCode(); }
 41+ function getURLVariant() { return ''; }
 42+ function getConvRuleTitle() { return false; }
 43+ function findVariantLink( &$l, &$n, $ignoreOtherCond = false ) { }
 44+ function getExtraHashOptions() { return ''; }
 45+ function getParsedTitle() { return ''; }
 46+ function markNoConversion( $text, $noParse = false ) { return $text; }
 47+ function convertCategoryKey( $key ) { return $key; }
 48+ function convertLinkToAllVariants( $text ) { return $this->autoConvertToAllVariants( $text ); }
 49+ function armourMath( $text ) { return $text; }
 50+}
 51+
 52+/**
 53+ * Internationalisation code
 54+ * @ingroup Language
 55+ */
 56+class Language {
 57+ var $mConverter, $mVariants, $mCode, $mLoaded = false;
 58+ var $mMagicExtensions = array(), $mMagicHookDone = false;
 59+
 60+ var $mNamespaceIds, $namespaceNames, $namespaceAliases;
 61+ var $dateFormatStrings = array();
 62+ var $mExtendedSpecialPageAliases;
 63+
 64+ /**
 65+ * ReplacementArray object caches
 66+ */
 67+ var $transformData = array();
 68+
 69+ /**
 70+ * @var LocalisationCache
 71+ */
 72+ static public $dataCache;
 73+
 74+ static public $mLangObjCache = array();
 75+
 76+ static public $mWeekdayMsgs = array(
 77+ 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
 78+ 'friday', 'saturday'
 79+ );
 80+
 81+ static public $mWeekdayAbbrevMsgs = array(
 82+ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
 83+ );
 84+
 85+ static public $mMonthMsgs = array(
 86+ 'january', 'february', 'march', 'april', 'may_long', 'june',
 87+ 'july', 'august', 'september', 'october', 'november',
 88+ 'december'
 89+ );
 90+ static public $mMonthGenMsgs = array(
 91+ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
 92+ 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
 93+ 'december-gen'
 94+ );
 95+ static public $mMonthAbbrevMsgs = array(
 96+ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
 97+ 'sep', 'oct', 'nov', 'dec'
 98+ );
 99+
 100+ static public $mIranianCalendarMonthMsgs = array(
 101+ 'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3',
 102+ 'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6',
 103+ 'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9',
 104+ 'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12'
 105+ );
 106+
 107+ static public $mHebrewCalendarMonthMsgs = array(
 108+ 'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3',
 109+ 'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6',
 110+ 'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9',
 111+ 'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12',
 112+ 'hebrew-calendar-m6a', 'hebrew-calendar-m6b'
 113+ );
 114+
 115+ static public $mHebrewCalendarMonthGenMsgs = array(
 116+ 'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen',
 117+ 'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen',
 118+ 'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen',
 119+ 'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen',
 120+ 'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen'
 121+ );
 122+
 123+ static public $mHijriCalendarMonthMsgs = array(
 124+ 'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3',
 125+ 'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6',
 126+ 'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9',
 127+ 'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12'
 128+ );
 129+
 130+ /**
 131+ * Get a cached language object for a given language code
 132+ * @param $code String
 133+ * @return Language
 134+ */
 135+ static function factory( $code ) {
 136+ if ( !isset( self::$mLangObjCache[$code] ) ) {
 137+ if ( count( self::$mLangObjCache ) > 10 ) {
 138+ // Don't keep a billion objects around, that's stupid.
 139+ self::$mLangObjCache = array();
 140+ }
 141+ self::$mLangObjCache[$code] = self::newFromCode( $code );
 142+ }
 143+ return self::$mLangObjCache[$code];
 144+ }
 145+
 146+ /**
 147+ * Create a language object for a given language code
 148+ * @param $code String
 149+ * @return Language
 150+ */
 151+ protected static function newFromCode( $code ) {
 152+ global $IP;
 153+ static $recursionLevel = 0;
 154+
 155+ // Protect against path traversal below
 156+ if ( !Language::isValidCode( $code )
 157+ || strcspn( $code, ":/\\\000" ) !== strlen( $code ) )
 158+ {
 159+ throw new MWException( "Invalid language code \"$code\"" );
 160+ }
 161+
 162+ if ( !Language::isValidBuiltInCode( $code ) ) {
 163+ // It's not possible to customise this code with class files, so
 164+ // just return a Language object. This is to support uselang= hacks.
 165+ $lang = new Language;
 166+ $lang->setCode( $code );
 167+ return $lang;
 168+ }
 169+
 170+ if ( $code == 'en' ) {
 171+ $class = 'Language';
 172+ } else {
 173+ $class = 'Language' . str_replace( '-', '_', ucfirst( $code ) );
 174+ if ( !defined( 'MW_COMPILED' ) ) {
 175+ // Preload base classes to work around APC/PHP5 bug
 176+ if ( file_exists( "$IP/languages/classes/$class.deps.php" ) ) {
 177+ include_once( "$IP/languages/classes/$class.deps.php" );
 178+ }
 179+ if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
 180+ include_once( "$IP/languages/classes/$class.php" );
 181+ }
 182+ }
 183+ }
 184+
 185+ if ( $recursionLevel > 5 ) {
 186+ throw new MWException( "Language fallback loop detected when creating class $class\n" );
 187+ }
 188+
 189+ if ( !MWInit::classExists( $class ) ) {
 190+ $fallback = Language::getFallbackFor( $code );
 191+ ++$recursionLevel;
 192+ $lang = Language::newFromCode( $fallback );
 193+ --$recursionLevel;
 194+ $lang->setCode( $code );
 195+ } else {
 196+ $lang = new $class;
 197+ }
 198+ return $lang;
 199+ }
 200+
 201+ /**
 202+ * Returns true if a language code string is of a valid form, whether or
 203+ * not it exists. This includes codes which are used solely for
 204+ * customisation via the MediaWiki namespace.
 205+ *
 206+ * @param $code string
 207+ *
 208+ * @return bool
 209+ */
 210+ public static function isValidCode( $code ) {
 211+ return
 212+ strcspn( $code, ":/\\\000" ) === strlen( $code )
 213+ && !preg_match( Title::getTitleInvalidRegex(), $code );
 214+ }
 215+
 216+ /**
 217+ * Returns true if a language code is of a valid form for the purposes of
 218+ * internal customisation of MediaWiki, via Messages*.php.
 219+ *
 220+ * @param $code string
 221+ *
 222+ * @return bool
 223+ */
 224+ public static function isValidBuiltInCode( $code ) {
 225+ return preg_match( '/^[a-z0-9-]*$/i', $code );
 226+ }
 227+
 228+ /**
 229+ * Get the LocalisationCache instance
 230+ *
 231+ * @return LocalisationCache
 232+ */
 233+ public static function getLocalisationCache() {
 234+ if ( is_null( self::$dataCache ) ) {
 235+ global $wgLocalisationCacheConf;
 236+ $class = $wgLocalisationCacheConf['class'];
 237+ self::$dataCache = new $class( $wgLocalisationCacheConf );
 238+ }
 239+ return self::$dataCache;
 240+ }
 241+
 242+ function __construct() {
 243+ // Set the code to the name of the descendant
 244+ if ( get_class( $this ) == 'Language' ) {
 245+ $this->mCode = 'en';
 246+ } else {
 247+ $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) );
 248+ }
 249+ }
 250+
 251+ /**
 252+ * Reduce memory usage
 253+ */
 254+ function __destruct() {
 255+ foreach ( $this as $name => $value ) {
 256+ unset( $this->$name );
 257+ }
 258+ }
 259+
 260+ /**
 261+ * Hook which will be called if this is the content language.
 262+ * Descendants can use this to register hook functions or modify globals
 263+ */
 264+ function initContLang() { }
 265+
 266+ /**
 267+ * @return array|bool
 268+ */
 269+ function getFallbackLanguageCode() {
 270+ if ( $this->mCode === 'en' ) {
 271+ return false;
 272+ } else {
 273+ return self::$dataCache->getItem( $this->mCode, 'fallback' );
 274+ }
 275+ }
 276+
 277+ /**
 278+ * Exports $wgBookstoreListEn
 279+ * @return array
 280+ */
 281+ function getBookstoreList() {
 282+ return self::$dataCache->getItem( $this->mCode, 'bookstoreList' );
 283+ }
 284+
 285+ /**
 286+ * @return array
 287+ */
 288+ function getNamespaces() {
 289+ if ( is_null( $this->namespaceNames ) ) {
 290+ global $wgMetaNamespace, $wgMetaNamespaceTalk, $wgExtraNamespaces;
 291+
 292+ $this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
 293+ $validNamespaces = MWNamespace::getCanonicalNamespaces();
 294+
 295+ $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames + $validNamespaces;
 296+
 297+ $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
 298+ if ( $wgMetaNamespaceTalk ) {
 299+ $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
 300+ } else {
 301+ $talk = $this->namespaceNames[NS_PROJECT_TALK];
 302+ $this->namespaceNames[NS_PROJECT_TALK] =
 303+ $this->fixVariableInNamespace( $talk );
 304+ }
 305+
 306+ # Sometimes a language will be localised but not actually exist on this wiki.
 307+ foreach( $this->namespaceNames as $key => $text ) {
 308+ if ( !isset( $validNamespaces[$key] ) ) {
 309+ unset( $this->namespaceNames[$key] );
 310+ }
 311+ }
 312+
 313+ # The above mixing may leave namespaces out of canonical order.
 314+ # Re-order by namespace ID number...
 315+ ksort( $this->namespaceNames );
 316+ }
 317+ return $this->namespaceNames;
 318+ }
 319+
 320+ /**
 321+ * A convenience function that returns the same thing as
 322+ * getNamespaces() except with the array values changed to ' '
 323+ * where it found '_', useful for producing output to be displayed
 324+ * e.g. in <select> forms.
 325+ *
 326+ * @return array
 327+ */
 328+ function getFormattedNamespaces() {
 329+ $ns = $this->getNamespaces();
 330+ foreach ( $ns as $k => $v ) {
 331+ $ns[$k] = strtr( $v, '_', ' ' );
 332+ }
 333+ return $ns;
 334+ }
 335+
 336+ /**
 337+ * Get a namespace value by key
 338+ * <code>
 339+ * $mw_ns = $wgContLang->getNsText( NS_MEDIAWIKI );
 340+ * echo $mw_ns; // prints 'MediaWiki'
 341+ * </code>
 342+ *
 343+ * @param $index Int: the array key of the namespace to return
 344+ * @return mixed, string if the namespace value exists, otherwise false
 345+ */
 346+ function getNsText( $index ) {
 347+ $ns = $this->getNamespaces();
 348+ return isset( $ns[$index] ) ? $ns[$index] : false;
 349+ }
 350+
 351+ /**
 352+ * A convenience function that returns the same thing as
 353+ * getNsText() except with '_' changed to ' ', useful for
 354+ * producing output.
 355+ *
 356+ * @param $index string
 357+ *
 358+ * @return array
 359+ */
 360+ function getFormattedNsText( $index ) {
 361+ $ns = $this->getNsText( $index );
 362+ return strtr( $ns, '_', ' ' );
 363+ }
 364+
 365+ /**
 366+ * Returns gender-dependent namespace alias if available.
 367+ * @param $index Int: namespace index
 368+ * @param $gender String: gender key (male, female... )
 369+ * @return String
 370+ * @since 1.18
 371+ */
 372+ function getGenderNsText( $index, $gender ) {
 373+ $ns = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
 374+ return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->getNsText( $index );
 375+ }
 376+
 377+ /**
 378+ * Whether this language makes distinguishes genders for example in
 379+ * namespaces.
 380+ * @return bool
 381+ * @since 1.18
 382+ */
 383+ function needsGenderDistinction() {
 384+ $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
 385+ return count( $aliases ) > 0;
 386+ }
 387+
 388+ /**
 389+ * Get a namespace key by value, case insensitive.
 390+ * Only matches namespace names for the current language, not the
 391+ * canonical ones defined in Namespace.php.
 392+ *
 393+ * @param $text String
 394+ * @return mixed An integer if $text is a valid value otherwise false
 395+ */
 396+ function getLocalNsIndex( $text ) {
 397+ $lctext = $this->lc( $text );
 398+ $ids = $this->getNamespaceIds();
 399+ return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
 400+ }
 401+
 402+ /**
 403+ * @return array
 404+ */
 405+ function getNamespaceAliases() {
 406+ if ( is_null( $this->namespaceAliases ) ) {
 407+ $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' );
 408+ if ( !$aliases ) {
 409+ $aliases = array();
 410+ } else {
 411+ foreach ( $aliases as $name => $index ) {
 412+ if ( $index === NS_PROJECT_TALK ) {
 413+ unset( $aliases[$name] );
 414+ $name = $this->fixVariableInNamespace( $name );
 415+ $aliases[$name] = $index;
 416+ }
 417+ }
 418+ }
 419+
 420+ $genders = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
 421+ foreach ( $genders as $index => $forms ) {
 422+ foreach ( $forms as $alias ) {
 423+ $aliases[$alias] = $index;
 424+ }
 425+ }
 426+
 427+ $this->namespaceAliases = $aliases;
 428+ }
 429+ return $this->namespaceAliases;
 430+ }
 431+
 432+ /**
 433+ * @return array
 434+ */
 435+ function getNamespaceIds() {
 436+ if ( is_null( $this->mNamespaceIds ) ) {
 437+ global $wgNamespaceAliases;
 438+ # Put namespace names and aliases into a hashtable.
 439+ # If this is too slow, then we should arrange it so that it is done
 440+ # before caching. The catch is that at pre-cache time, the above
 441+ # class-specific fixup hasn't been done.
 442+ $this->mNamespaceIds = array();
 443+ foreach ( $this->getNamespaces() as $index => $name ) {
 444+ $this->mNamespaceIds[$this->lc( $name )] = $index;
 445+ }
 446+ foreach ( $this->getNamespaceAliases() as $name => $index ) {
 447+ $this->mNamespaceIds[$this->lc( $name )] = $index;
 448+ }
 449+ if ( $wgNamespaceAliases ) {
 450+ foreach ( $wgNamespaceAliases as $name => $index ) {
 451+ $this->mNamespaceIds[$this->lc( $name )] = $index;
 452+ }
 453+ }
 454+ }
 455+ return $this->mNamespaceIds;
 456+ }
 457+
 458+
 459+ /**
 460+ * Get a namespace key by value, case insensitive. Canonical namespace
 461+ * names override custom ones defined for the current language.
 462+ *
 463+ * @param $text String
 464+ * @return mixed An integer if $text is a valid value otherwise false
 465+ */
 466+ function getNsIndex( $text ) {
 467+ $lctext = $this->lc( $text );
 468+ if ( ( $ns = MWNamespace::getCanonicalIndex( $lctext ) ) !== null ) {
 469+ return $ns;
 470+ }
 471+ $ids = $this->getNamespaceIds();
 472+ return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
 473+ }
 474+
 475+ /**
 476+ * short names for language variants used for language conversion links.
 477+ *
 478+ * @param $code String
 479+ * @return string
 480+ */
 481+ function getVariantname( $code ) {
 482+ return $this->getMessageFromDB( "variantname-$code" );
 483+ }
 484+
 485+ /**
 486+ * @param $name string
 487+ * @return string
 488+ */
 489+ function specialPage( $name ) {
 490+ $aliases = $this->getSpecialPageAliases();
 491+ if ( isset( $aliases[$name][0] ) ) {
 492+ $name = $aliases[$name][0];
 493+ }
 494+ return $this->getNsText( NS_SPECIAL ) . ':' . $name;
 495+ }
 496+
 497+ /**
 498+ * @return array
 499+ */
 500+ function getQuickbarSettings() {
 501+ return array(
 502+ $this->getMessage( 'qbsettings-none' ),
 503+ $this->getMessage( 'qbsettings-fixedleft' ),
 504+ $this->getMessage( 'qbsettings-fixedright' ),
 505+ $this->getMessage( 'qbsettings-floatingleft' ),
 506+ $this->getMessage( 'qbsettings-floatingright' )
 507+ );
 508+ }
 509+
 510+ /**
 511+ * @return array
 512+ */
 513+ function getDatePreferences() {
 514+ return self::$dataCache->getItem( $this->mCode, 'datePreferences' );
 515+ }
 516+
 517+ /**
 518+ * @return array
 519+ */
 520+ function getDateFormats() {
 521+ return self::$dataCache->getItem( $this->mCode, 'dateFormats' );
 522+ }
 523+
 524+ /**
 525+ * @return array|string
 526+ */
 527+ function getDefaultDateFormat() {
 528+ $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat' );
 529+ if ( $df === 'dmy or mdy' ) {
 530+ global $wgAmericanDates;
 531+ return $wgAmericanDates ? 'mdy' : 'dmy';
 532+ } else {
 533+ return $df;
 534+ }
 535+ }
 536+
 537+ /**
 538+ * @return array
 539+ */
 540+ function getDatePreferenceMigrationMap() {
 541+ return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' );
 542+ }
 543+
 544+ /**
 545+ * @param $image
 546+ * @return array|null
 547+ */
 548+ function getImageFile( $image ) {
 549+ return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image );
 550+ }
 551+
 552+ /**
 553+ * @return array
 554+ */
 555+ function getDefaultUserOptionOverrides() {
 556+ return self::$dataCache->getItem( $this->mCode, 'defaultUserOptionOverrides' );
 557+ }
 558+
 559+ /**
 560+ * @return array
 561+ */
 562+ function getExtraUserToggles() {
 563+ return self::$dataCache->getItem( $this->mCode, 'extraUserToggles' );
 564+ }
 565+
 566+ /**
 567+ * @param $tog
 568+ * @return string
 569+ */
 570+ function getUserToggle( $tog ) {
 571+ return $this->getMessageFromDB( "tog-$tog" );
 572+ }
 573+
 574+ /**
 575+ * Get language names, indexed by code.
 576+ * If $customisedOnly is true, only returns codes with a messages file
 577+ *
 578+ * @param $customisedOnly bool
 579+ *
 580+ * @return array
 581+ */
 582+ public static function getLanguageNames( $customisedOnly = false ) {
 583+ global $wgExtraLanguageNames;
 584+ static $coreLanguageNames;
 585+
 586+ if ( $coreLanguageNames === null ) {
 587+ include( MWInit::compiledPath( 'languages/Names.php' ) );
 588+ }
 589+
 590+ $allNames = $wgExtraLanguageNames + $coreLanguageNames;
 591+ if ( !$customisedOnly ) {
 592+ return $allNames;
 593+ }
 594+
 595+ global $IP;
 596+ $names = array();
 597+ $dir = opendir( "$IP/languages/messages" );
 598+ while ( false !== ( $file = readdir( $dir ) ) ) {
 599+ $code = self::getCodeFromFileName( $file, 'Messages' );
 600+ if ( $code && isset( $allNames[$code] ) ) {
 601+ $names[$code] = $allNames[$code];
 602+ }
 603+ }
 604+ closedir( $dir );
 605+ return $names;
 606+ }
 607+
 608+ /**
 609+ * Get translated language names. This is done on best effort and
 610+ * by default this is exactly the same as Language::getLanguageNames.
 611+ * The CLDR extension provides translated names.
 612+ * @param $code String Language code.
 613+ * @return Array language code => language name
 614+ * @since 1.18.0
 615+ */
 616+ public static function getTranslatedLanguageNames( $code ) {
 617+ $names = array();
 618+ wfRunHooks( 'LanguageGetTranslatedLanguageNames', array( &$names, $code ) );
 619+
 620+ foreach ( self::getLanguageNames() as $code => $name ) {
 621+ if ( !isset( $names[$code] ) ) $names[$code] = $name;
 622+ }
 623+
 624+ return $names;
 625+ }
 626+
 627+ /**
 628+ * Get a message from the MediaWiki namespace.
 629+ *
 630+ * @param $msg String: message name
 631+ * @return string
 632+ */
 633+ function getMessageFromDB( $msg ) {
 634+ return wfMsgExt( $msg, array( 'parsemag', 'language' => $this ) );
 635+ }
 636+
 637+ /**
 638+ * @param $code string
 639+ * @return string
 640+ */
 641+ function getLanguageName( $code ) {
 642+ $names = self::getLanguageNames();
 643+ if ( !array_key_exists( $code, $names ) ) {
 644+ return '';
 645+ }
 646+ return $names[$code];
 647+ }
 648+
 649+ /**
 650+ * @param $key string
 651+ * @return string
 652+ */
 653+ function getMonthName( $key ) {
 654+ return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] );
 655+ }
 656+
 657+ /**
 658+ * @return array
 659+ */
 660+ function getMonthNamesArray() {
 661+ $monthNames = array( '' );
 662+ for ( $i=1; $i < 13; $i++ ) {
 663+ $monthNames[] = $this->getMonthName( $i );
 664+ }
 665+ return $monthNames;
 666+ }
 667+
 668+ /**
 669+ * @param $key string
 670+ * @return string
 671+ */
 672+ function getMonthNameGen( $key ) {
 673+ return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] );
 674+ }
 675+
 676+ /**
 677+ * @param $key string
 678+ * @return string
 679+ */
 680+ function getMonthAbbreviation( $key ) {
 681+ return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] );
 682+ }
 683+
 684+ /**
 685+ * @return array
 686+ */
 687+ function getMonthAbbreviationsArray() {
 688+ $monthNames = array( '' );
 689+ for ( $i=1; $i < 13; $i++ ) {
 690+ $monthNames[] = $this->getMonthAbbreviation( $i );
 691+ }
 692+ return $monthNames;
 693+ }
 694+
 695+ /**
 696+ * @param $key string
 697+ * @return string
 698+ */
 699+ function getWeekdayName( $key ) {
 700+ return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] );
 701+ }
 702+
 703+ /**
 704+ * @param $key string
 705+ * @return string
 706+ */
 707+ function getWeekdayAbbreviation( $key ) {
 708+ return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] );
 709+ }
 710+
 711+ /**
 712+ * @param $key string
 713+ * @return string
 714+ */
 715+ function getIranianCalendarMonthName( $key ) {
 716+ return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
 717+ }
 718+
 719+ /**
 720+ * @param $key string
 721+ * @return string
 722+ */
 723+ function getHebrewCalendarMonthName( $key ) {
 724+ return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
 725+ }
 726+
 727+ /**
 728+ * @param $key string
 729+ * @return string
 730+ */
 731+ function getHebrewCalendarMonthNameGen( $key ) {
 732+ return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
 733+ }
 734+
 735+ /**
 736+ * @param $key string
 737+ * @return string
 738+ */
 739+ function getHijriCalendarMonthName( $key ) {
 740+ return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
 741+ }
 742+
 743+ /**
 744+ * Used by date() and time() to adjust the time output.
 745+ *
 746+ * @param $ts Int the time in date('YmdHis') format
 747+ * @param $tz Mixed: adjust the time by this amount (default false, mean we
 748+ * get user timecorrection setting)
 749+ * @return int
 750+ */
 751+ function userAdjust( $ts, $tz = false ) {
 752+ global $wgUser, $wgLocalTZoffset;
 753+
 754+ if ( $tz === false ) {
 755+ $tz = $wgUser->getOption( 'timecorrection' );
 756+ }
 757+
 758+ $data = explode( '|', $tz, 3 );
 759+
 760+ if ( $data[0] == 'ZoneInfo' ) {
 761+ wfSuppressWarnings();
 762+ $userTZ = timezone_open( $data[2] );
 763+ wfRestoreWarnings();
 764+ if ( $userTZ !== false ) {
 765+ $date = date_create( $ts, timezone_open( 'UTC' ) );
 766+ date_timezone_set( $date, $userTZ );
 767+ $date = date_format( $date, 'YmdHis' );
 768+ return $date;
 769+ }
 770+ # Unrecognized timezone, default to 'Offset' with the stored offset.
 771+ $data[0] = 'Offset';
 772+ }
 773+
 774+ $minDiff = 0;
 775+ if ( $data[0] == 'System' || $tz == '' ) {
 776+ #  Global offset in minutes.
 777+ if ( isset( $wgLocalTZoffset ) ) {
 778+ $minDiff = $wgLocalTZoffset;
 779+ }
 780+ } elseif ( $data[0] == 'Offset' ) {
 781+ $minDiff = intval( $data[1] );
 782+ } else {
 783+ $data = explode( ':', $tz );
 784+ if ( count( $data ) == 2 ) {
 785+ $data[0] = intval( $data[0] );
 786+ $data[1] = intval( $data[1] );
 787+ $minDiff = abs( $data[0] ) * 60 + $data[1];
 788+ if ( $data[0] < 0 ) {
 789+ $minDiff = -$minDiff;
 790+ }
 791+ } else {
 792+ $minDiff = intval( $data[0] ) * 60;
 793+ }
 794+ }
 795+
 796+ # No difference ? Return time unchanged
 797+ if ( 0 == $minDiff ) {
 798+ return $ts;
 799+ }
 800+
 801+ wfSuppressWarnings(); // E_STRICT system time bitching
 802+ # Generate an adjusted date; take advantage of the fact that mktime
 803+ # will normalize out-of-range values so we don't have to split $minDiff
 804+ # into hours and minutes.
 805+ $t = mktime( (
 806+ (int)substr( $ts, 8, 2 ) ), # Hours
 807+ (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
 808+ (int)substr( $ts, 12, 2 ), # Seconds
 809+ (int)substr( $ts, 4, 2 ), # Month
 810+ (int)substr( $ts, 6, 2 ), # Day
 811+ (int)substr( $ts, 0, 4 ) ); # Year
 812+
 813+ $date = date( 'YmdHis', $t );
 814+ wfRestoreWarnings();
 815+
 816+ return $date;
 817+ }
 818+
 819+ /**
 820+ * This is a workalike of PHP's date() function, but with better
 821+ * internationalisation, a reduced set of format characters, and a better
 822+ * escaping format.
 823+ *
 824+ * Supported format characters are dDjlNwzWFmMntLoYyaAgGhHiscrU. See the
 825+ * PHP manual for definitions. There are a number of extensions, which
 826+ * start with "x":
 827+ *
 828+ * xn Do not translate digits of the next numeric format character
 829+ * xN Toggle raw digit (xn) flag, stays set until explicitly unset
 830+ * xr Use roman numerals for the next numeric format character
 831+ * xh Use hebrew numerals for the next numeric format character
 832+ * xx Literal x
 833+ * xg Genitive month name
 834+ *
 835+ * xij j (day number) in Iranian calendar
 836+ * xiF F (month name) in Iranian calendar
 837+ * xin n (month number) in Iranian calendar
 838+ * xiY Y (full year) in Iranian calendar
 839+ *
 840+ * xjj j (day number) in Hebrew calendar
 841+ * xjF F (month name) in Hebrew calendar
 842+ * xjt t (days in month) in Hebrew calendar
 843+ * xjx xg (genitive month name) in Hebrew calendar
 844+ * xjn n (month number) in Hebrew calendar
 845+ * xjY Y (full year) in Hebrew calendar
 846+ *
 847+ * xmj j (day number) in Hijri calendar
 848+ * xmF F (month name) in Hijri calendar
 849+ * xmn n (month number) in Hijri calendar
 850+ * xmY Y (full year) in Hijri calendar
 851+ *
 852+ * xkY Y (full year) in Thai solar calendar. Months and days are
 853+ * identical to the Gregorian calendar
 854+ * xoY Y (full year) in Minguo calendar or Juche year.
 855+ * Months and days are identical to the
 856+ * Gregorian calendar
 857+ * xtY Y (full year) in Japanese nengo. Months and days are
 858+ * identical to the Gregorian calendar
 859+ *
 860+ * Characters enclosed in double quotes will be considered literal (with
 861+ * the quotes themselves removed). Unmatched quotes will be considered
 862+ * literal quotes. Example:
 863+ *
 864+ * "The month is" F => The month is January
 865+ * i's" => 20'11"
 866+ *
 867+ * Backslash escaping is also supported.
 868+ *
 869+ * Input timestamp is assumed to be pre-normalized to the desired local
 870+ * time zone, if any.
 871+ *
 872+ * @param $format String
 873+ * @param $ts String: 14-character timestamp
 874+ * YYYYMMDDHHMMSS
 875+ * 01234567890123
 876+ * @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai?
 877+ *
 878+ * @return string
 879+ */
 880+ function sprintfDate( $format, $ts ) {
 881+ $s = '';
 882+ $raw = false;
 883+ $roman = false;
 884+ $hebrewNum = false;
 885+ $unix = false;
 886+ $rawToggle = false;
 887+ $iranian = false;
 888+ $hebrew = false;
 889+ $hijri = false;
 890+ $thai = false;
 891+ $minguo = false;
 892+ $tenno = false;
 893+ for ( $p = 0; $p < strlen( $format ); $p++ ) {
 894+ $num = false;
 895+ $code = $format[$p];
 896+ if ( $code == 'x' && $p < strlen( $format ) - 1 ) {
 897+ $code .= $format[++$p];
 898+ }
 899+
 900+ if ( ( $code === 'xi' || $code == 'xj' || $code == 'xk' || $code == 'xm' || $code == 'xo' || $code == 'xt' ) && $p < strlen( $format ) - 1 ) {
 901+ $code .= $format[++$p];
 902+ }
 903+
 904+ switch ( $code ) {
 905+ case 'xx':
 906+ $s .= 'x';
 907+ break;
 908+ case 'xn':
 909+ $raw = true;
 910+ break;
 911+ case 'xN':
 912+ $rawToggle = !$rawToggle;
 913+ break;
 914+ case 'xr':
 915+ $roman = true;
 916+ break;
 917+ case 'xh':
 918+ $hebrewNum = true;
 919+ break;
 920+ case 'xg':
 921+ $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
 922+ break;
 923+ case 'xjx':
 924+ if ( !$hebrew ) $hebrew = self::tsToHebrew( $ts );
 925+ $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
 926+ break;
 927+ case 'd':
 928+ $num = substr( $ts, 6, 2 );
 929+ break;
 930+ case 'D':
 931+ if ( !$unix ) $unix = wfTimestamp( TS_UNIX, $ts );
 932+ $s .= $this->getWeekdayAbbreviation( gmdate( 'w', $unix ) + 1 );
 933+ break;
 934+ case 'j':
 935+ $num = intval( substr( $ts, 6, 2 ) );
 936+ break;
 937+ case 'xij':
 938+ if ( !$iranian ) {
 939+ $iranian = self::tsToIranian( $ts );
 940+ }
 941+ $num = $iranian[2];
 942+ break;
 943+ case 'xmj':
 944+ if ( !$hijri ) {
 945+ $hijri = self::tsToHijri( $ts );
 946+ }
 947+ $num = $hijri[2];
 948+ break;
 949+ case 'xjj':
 950+ if ( !$hebrew ) {
 951+ $hebrew = self::tsToHebrew( $ts );
 952+ }
 953+ $num = $hebrew[2];
 954+ break;
 955+ case 'l':
 956+ if ( !$unix ) {
 957+ $unix = wfTimestamp( TS_UNIX, $ts );
 958+ }
 959+ $s .= $this->getWeekdayName( gmdate( 'w', $unix ) + 1 );
 960+ break;
 961+ case 'N':
 962+ if ( !$unix ) {
 963+ $unix = wfTimestamp( TS_UNIX, $ts );
 964+ }
 965+ $w = gmdate( 'w', $unix );
 966+ $num = $w ? $w : 7;
 967+ break;
 968+ case 'w':
 969+ if ( !$unix ) {
 970+ $unix = wfTimestamp( TS_UNIX, $ts );
 971+ }
 972+ $num = gmdate( 'w', $unix );
 973+ break;
 974+ case 'z':
 975+ if ( !$unix ) {
 976+ $unix = wfTimestamp( TS_UNIX, $ts );
 977+ }
 978+ $num = gmdate( 'z', $unix );
 979+ break;
 980+ case 'W':
 981+ if ( !$unix ) {
 982+ $unix = wfTimestamp( TS_UNIX, $ts );
 983+ }
 984+ $num = gmdate( 'W', $unix );
 985+ break;
 986+ case 'F':
 987+ $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
 988+ break;
 989+ case 'xiF':
 990+ if ( !$iranian ) {
 991+ $iranian = self::tsToIranian( $ts );
 992+ }
 993+ $s .= $this->getIranianCalendarMonthName( $iranian[1] );
 994+ break;
 995+ case 'xmF':
 996+ if ( !$hijri ) {
 997+ $hijri = self::tsToHijri( $ts );
 998+ }
 999+ $s .= $this->getHijriCalendarMonthName( $hijri[1] );
 1000+ break;
 1001+ case 'xjF':
 1002+ if ( !$hebrew ) {
 1003+ $hebrew = self::tsToHebrew( $ts );
 1004+ }
 1005+ $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
 1006+ break;
 1007+ case 'm':
 1008+ $num = substr( $ts, 4, 2 );
 1009+ break;
 1010+ case 'M':
 1011+ $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
 1012+ break;
 1013+ case 'n':
 1014+ $num = intval( substr( $ts, 4, 2 ) );
 1015+ break;
 1016+ case 'xin':
 1017+ if ( !$iranian ) {
 1018+ $iranian = self::tsToIranian( $ts );
 1019+ }
 1020+ $num = $iranian[1];
 1021+ break;
 1022+ case 'xmn':
 1023+ if ( !$hijri ) {
 1024+ $hijri = self::tsToHijri ( $ts );
 1025+ }
 1026+ $num = $hijri[1];
 1027+ break;
 1028+ case 'xjn':
 1029+ if ( !$hebrew ) {
 1030+ $hebrew = self::tsToHebrew( $ts );
 1031+ }
 1032+ $num = $hebrew[1];
 1033+ break;
 1034+ case 't':
 1035+ if ( !$unix ) {
 1036+ $unix = wfTimestamp( TS_UNIX, $ts );
 1037+ }
 1038+ $num = gmdate( 't', $unix );
 1039+ break;
 1040+ case 'xjt':
 1041+ if ( !$hebrew ) {
 1042+ $hebrew = self::tsToHebrew( $ts );
 1043+ }
 1044+ $num = $hebrew[3];
 1045+ break;
 1046+ case 'L':
 1047+ if ( !$unix ) {
 1048+ $unix = wfTimestamp( TS_UNIX, $ts );
 1049+ }
 1050+ $num = gmdate( 'L', $unix );
 1051+ break;
 1052+ case 'o':
 1053+ if ( !$unix ) {
 1054+ $unix = wfTimestamp( TS_UNIX, $ts );
 1055+ }
 1056+ $num = date( 'o', $unix );
 1057+ break;
 1058+ case 'Y':
 1059+ $num = substr( $ts, 0, 4 );
 1060+ break;
 1061+ case 'xiY':
 1062+ if ( !$iranian ) {
 1063+ $iranian = self::tsToIranian( $ts );
 1064+ }
 1065+ $num = $iranian[0];
 1066+ break;
 1067+ case 'xmY':
 1068+ if ( !$hijri ) {
 1069+ $hijri = self::tsToHijri( $ts );
 1070+ }
 1071+ $num = $hijri[0];
 1072+ break;
 1073+ case 'xjY':
 1074+ if ( !$hebrew ) {
 1075+ $hebrew = self::tsToHebrew( $ts );
 1076+ }
 1077+ $num = $hebrew[0];
 1078+ break;
 1079+ case 'xkY':
 1080+ if ( !$thai ) {
 1081+ $thai = self::tsToYear( $ts, 'thai' );
 1082+ }
 1083+ $num = $thai[0];
 1084+ break;
 1085+ case 'xoY':
 1086+ if ( !$minguo ) {
 1087+ $minguo = self::tsToYear( $ts, 'minguo' );
 1088+ }
 1089+ $num = $minguo[0];
 1090+ break;
 1091+ case 'xtY':
 1092+ if ( !$tenno ) {
 1093+ $tenno = self::tsToYear( $ts, 'tenno' );
 1094+ }
 1095+ $num = $tenno[0];
 1096+ break;
 1097+ case 'y':
 1098+ $num = substr( $ts, 2, 2 );
 1099+ break;
 1100+ case 'a':
 1101+ $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
 1102+ break;
 1103+ case 'A':
 1104+ $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
 1105+ break;
 1106+ case 'g':
 1107+ $h = substr( $ts, 8, 2 );
 1108+ $num = $h % 12 ? $h % 12 : 12;
 1109+ break;
 1110+ case 'G':
 1111+ $num = intval( substr( $ts, 8, 2 ) );
 1112+ break;
 1113+ case 'h':
 1114+ $h = substr( $ts, 8, 2 );
 1115+ $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
 1116+ break;
 1117+ case 'H':
 1118+ $num = substr( $ts, 8, 2 );
 1119+ break;
 1120+ case 'i':
 1121+ $num = substr( $ts, 10, 2 );
 1122+ break;
 1123+ case 's':
 1124+ $num = substr( $ts, 12, 2 );
 1125+ break;
 1126+ case 'c':
 1127+ if ( !$unix ) {
 1128+ $unix = wfTimestamp( TS_UNIX, $ts );
 1129+ }
 1130+ $s .= gmdate( 'c', $unix );
 1131+ break;
 1132+ case 'r':
 1133+ if ( !$unix ) {
 1134+ $unix = wfTimestamp( TS_UNIX, $ts );
 1135+ }
 1136+ $s .= gmdate( 'r', $unix );
 1137+ break;
 1138+ case 'U':
 1139+ if ( !$unix ) {
 1140+ $unix = wfTimestamp( TS_UNIX, $ts );
 1141+ }
 1142+ $num = $unix;
 1143+ break;
 1144+ case '\\':
 1145+ # Backslash escaping
 1146+ if ( $p < strlen( $format ) - 1 ) {
 1147+ $s .= $format[++$p];
 1148+ } else {
 1149+ $s .= '\\';
 1150+ }
 1151+ break;
 1152+ case '"':
 1153+ # Quoted literal
 1154+ if ( $p < strlen( $format ) - 1 ) {
 1155+ $endQuote = strpos( $format, '"', $p + 1 );
 1156+ if ( $endQuote === false ) {
 1157+ # No terminating quote, assume literal "
 1158+ $s .= '"';
 1159+ } else {
 1160+ $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
 1161+ $p = $endQuote;
 1162+ }
 1163+ } else {
 1164+ # Quote at end of string, assume literal "
 1165+ $s .= '"';
 1166+ }
 1167+ break;
 1168+ default:
 1169+ $s .= $format[$p];
 1170+ }
 1171+ if ( $num !== false ) {
 1172+ if ( $rawToggle || $raw ) {
 1173+ $s .= $num;
 1174+ $raw = false;
 1175+ } elseif ( $roman ) {
 1176+ $s .= self::romanNumeral( $num );
 1177+ $roman = false;
 1178+ } elseif ( $hebrewNum ) {
 1179+ $s .= self::hebrewNumeral( $num );
 1180+ $hebrewNum = false;
 1181+ } else {
 1182+ $s .= $this->formatNum( $num, true );
 1183+ }
 1184+ }
 1185+ }
 1186+ return $s;
 1187+ }
 1188+
 1189+ private static $GREG_DAYS = array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
 1190+ private static $IRANIAN_DAYS = array( 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 );
 1191+
 1192+ /**
 1193+ * Algorithm by Roozbeh Pournader and Mohammad Toossi to convert
 1194+ * Gregorian dates to Iranian dates. Originally written in C, it
 1195+ * is released under the terms of GNU Lesser General Public
 1196+ * License. Conversion to PHP was performed by Niklas Laxström.
 1197+ *
 1198+ * Link: http://www.farsiweb.info/jalali/jalali.c
 1199+ *
 1200+ * @param $ts string
 1201+ *
 1202+ * @return string
 1203+ */
 1204+ private static function tsToIranian( $ts ) {
 1205+ $gy = substr( $ts, 0, 4 ) -1600;
 1206+ $gm = substr( $ts, 4, 2 ) -1;
 1207+ $gd = substr( $ts, 6, 2 ) -1;
 1208+
 1209+ # Days passed from the beginning (including leap years)
 1210+ $gDayNo = 365 * $gy
 1211+ + floor( ( $gy + 3 ) / 4 )
 1212+ - floor( ( $gy + 99 ) / 100 )
 1213+ + floor( ( $gy + 399 ) / 400 );
 1214+
 1215+
 1216+ // Add days of the past months of this year
 1217+ for ( $i = 0; $i < $gm; $i++ ) {
 1218+ $gDayNo += self::$GREG_DAYS[$i];
 1219+ }
 1220+
 1221+ // Leap years
 1222+ if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
 1223+ $gDayNo++;
 1224+ }
 1225+
 1226+ // Days passed in current month
 1227+ $gDayNo += $gd;
 1228+
 1229+ $jDayNo = $gDayNo - 79;
 1230+
 1231+ $jNp = floor( $jDayNo / 12053 );
 1232+ $jDayNo %= 12053;
 1233+
 1234+ $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
 1235+ $jDayNo %= 1461;
 1236+
 1237+ if ( $jDayNo >= 366 ) {
 1238+ $jy += floor( ( $jDayNo - 1 ) / 365 );
 1239+ $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
 1240+ }
 1241+
 1242+ for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
 1243+ $jDayNo -= self::$IRANIAN_DAYS[$i];
 1244+ }
 1245+
 1246+ $jm = $i + 1;
 1247+ $jd = $jDayNo + 1;
 1248+
 1249+ return array( $jy, $jm, $jd );
 1250+ }
 1251+
 1252+ /**
 1253+ * Converting Gregorian dates to Hijri dates.
 1254+ *
 1255+ * Based on a PHP-Nuke block by Sharjeel which is released under GNU/GPL license
 1256+ *
 1257+ * @link http://phpnuke.org/modules.php?name=News&file=article&sid=8234&mode=thread&order=0&thold=0
 1258+ *
 1259+ * @param $ts string
 1260+ *
 1261+ * @return string
 1262+ */
 1263+ private static function tsToHijri( $ts ) {
 1264+ $year = substr( $ts, 0, 4 );
 1265+ $month = substr( $ts, 4, 2 );
 1266+ $day = substr( $ts, 6, 2 );
 1267+
 1268+ $zyr = $year;
 1269+ $zd = $day;
 1270+ $zm = $month;
 1271+ $zy = $zyr;
 1272+
 1273+ if (
 1274+ ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
 1275+ ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
 1276+ )
 1277+ {
 1278+ $zjd = (int)( ( 1461 * ( $zy + 4800 + (int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
 1279+ (int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
 1280+ (int)( ( 3 * (int)( ( ( $zy + 4900 + (int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
 1281+ $zd - 32075;
 1282+ } else {
 1283+ $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
 1284+ (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
 1285+ }
 1286+
 1287+ $zl = $zjd -1948440 + 10632;
 1288+ $zn = (int)( ( $zl - 1 ) / 10631 );
 1289+ $zl = $zl - 10631 * $zn + 354;
 1290+ $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) + ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
 1291+ $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) - ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
 1292+ $zm = (int)( ( 24 * $zl ) / 709 );
 1293+ $zd = $zl - (int)( ( 709 * $zm ) / 24 );
 1294+ $zy = 30 * $zn + $zj - 30;
 1295+
 1296+ return array( $zy, $zm, $zd );
 1297+ }
 1298+
 1299+ /**
 1300+ * Converting Gregorian dates to Hebrew dates.
 1301+ *
 1302+ * Based on a JavaScript code by Abu Mami and Yisrael Hersch
 1303+ * (abu-mami@kaluach.net, http://www.kaluach.net), who permitted
 1304+ * to translate the relevant functions into PHP and release them under
 1305+ * GNU GPL.
 1306+ *
 1307+ * The months are counted from Tishrei = 1. In a leap year, Adar I is 13
 1308+ * and Adar II is 14. In a non-leap year, Adar is 6.
 1309+ *
 1310+ * @param $ts string
 1311+ *
 1312+ * @return string
 1313+ */
 1314+ private static function tsToHebrew( $ts ) {
 1315+ # Parse date
 1316+ $year = substr( $ts, 0, 4 );
 1317+ $month = substr( $ts, 4, 2 );
 1318+ $day = substr( $ts, 6, 2 );
 1319+
 1320+ # Calculate Hebrew year
 1321+ $hebrewYear = $year + 3760;
 1322+
 1323+ # Month number when September = 1, August = 12
 1324+ $month += 4;
 1325+ if ( $month > 12 ) {
 1326+ # Next year
 1327+ $month -= 12;
 1328+ $year++;
 1329+ $hebrewYear++;
 1330+ }
 1331+
 1332+ # Calculate day of year from 1 September
 1333+ $dayOfYear = $day;
 1334+ for ( $i = 1; $i < $month; $i++ ) {
 1335+ if ( $i == 6 ) {
 1336+ # February
 1337+ $dayOfYear += 28;
 1338+ # Check if the year is leap
 1339+ if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
 1340+ $dayOfYear++;
 1341+ }
 1342+ } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
 1343+ $dayOfYear += 30;
 1344+ } else {
 1345+ $dayOfYear += 31;
 1346+ }
 1347+ }
 1348+
 1349+ # Calculate the start of the Hebrew year
 1350+ $start = self::hebrewYearStart( $hebrewYear );
 1351+
 1352+ # Calculate next year's start
 1353+ if ( $dayOfYear <= $start ) {
 1354+ # Day is before the start of the year - it is the previous year
 1355+ # Next year's start
 1356+ $nextStart = $start;
 1357+ # Previous year
 1358+ $year--;
 1359+ $hebrewYear--;
 1360+ # Add days since previous year's 1 September
 1361+ $dayOfYear += 365;
 1362+ if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
 1363+ # Leap year
 1364+ $dayOfYear++;
 1365+ }
 1366+ # Start of the new (previous) year
 1367+ $start = self::hebrewYearStart( $hebrewYear );
 1368+ } else {
 1369+ # Next year's start
 1370+ $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
 1371+ }
 1372+
 1373+ # Calculate Hebrew day of year
 1374+ $hebrewDayOfYear = $dayOfYear - $start;
 1375+
 1376+ # Difference between year's days
 1377+ $diff = $nextStart - $start;
 1378+ # Add 12 (or 13 for leap years) days to ignore the difference between
 1379+ # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
 1380+ # difference is only about the year type
 1381+ if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
 1382+ $diff += 13;
 1383+ } else {
 1384+ $diff += 12;
 1385+ }
 1386+
 1387+ # Check the year pattern, and is leap year
 1388+ # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
 1389+ # This is mod 30, to work on both leap years (which add 30 days of Adar I)
 1390+ # and non-leap years
 1391+ $yearPattern = $diff % 30;
 1392+ # Check if leap year
 1393+ $isLeap = $diff >= 30;
 1394+
 1395+ # Calculate day in the month from number of day in the Hebrew year
 1396+ # Don't check Adar - if the day is not in Adar, we will stop before;
 1397+ # if it is in Adar, we will use it to check if it is Adar I or Adar II
 1398+ $hebrewDay = $hebrewDayOfYear;
 1399+ $hebrewMonth = 1;
 1400+ $days = 0;
 1401+ while ( $hebrewMonth <= 12 ) {
 1402+ # Calculate days in this month
 1403+ if ( $isLeap && $hebrewMonth == 6 ) {
 1404+ # Adar in a leap year
 1405+ if ( $isLeap ) {
 1406+ # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
 1407+ $days = 30;
 1408+ if ( $hebrewDay <= $days ) {
 1409+ # Day in Adar I
 1410+ $hebrewMonth = 13;
 1411+ } else {
 1412+ # Subtract the days of Adar I
 1413+ $hebrewDay -= $days;
 1414+ # Try Adar II
 1415+ $days = 29;
 1416+ if ( $hebrewDay <= $days ) {
 1417+ # Day in Adar II
 1418+ $hebrewMonth = 14;
 1419+ }
 1420+ }
 1421+ }
 1422+ } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
 1423+ # Cheshvan in a complete year (otherwise as the rule below)
 1424+ $days = 30;
 1425+ } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
 1426+ # Kislev in an incomplete year (otherwise as the rule below)
 1427+ $days = 29;
 1428+ } else {
 1429+ # Odd months have 30 days, even have 29
 1430+ $days = 30 - ( $hebrewMonth - 1 ) % 2;
 1431+ }
 1432+ if ( $hebrewDay <= $days ) {
 1433+ # In the current month
 1434+ break;
 1435+ } else {
 1436+ # Subtract the days of the current month
 1437+ $hebrewDay -= $days;
 1438+ # Try in the next month
 1439+ $hebrewMonth++;
 1440+ }
 1441+ }
 1442+
 1443+ return array( $hebrewYear, $hebrewMonth, $hebrewDay, $days );
 1444+ }
 1445+
 1446+ /**
 1447+ * This calculates the Hebrew year start, as days since 1 September.
 1448+ * Based on Carl Friedrich Gauss algorithm for finding Easter date.
 1449+ * Used for Hebrew date.
 1450+ *
 1451+ * @param $year int
 1452+ *
 1453+ * @return string
 1454+ */
 1455+ private static function hebrewYearStart( $year ) {
 1456+ $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
 1457+ $b = intval( ( $year - 1 ) % 4 );
 1458+ $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
 1459+ if ( $m < 0 ) {
 1460+ $m--;
 1461+ }
 1462+ $Mar = intval( $m );
 1463+ if ( $m < 0 ) {
 1464+ $m++;
 1465+ }
 1466+ $m -= $Mar;
 1467+
 1468+ $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
 1469+ if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
 1470+ $Mar++;
 1471+ } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
 1472+ $Mar += 2;
 1473+ } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
 1474+ $Mar++;
 1475+ }
 1476+
 1477+ $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
 1478+ return $Mar;
 1479+ }
 1480+
 1481+ /**
 1482+ * Algorithm to convert Gregorian dates to Thai solar dates,
 1483+ * Minguo dates or Minguo dates.
 1484+ *
 1485+ * Link: http://en.wikipedia.org/wiki/Thai_solar_calendar
 1486+ * http://en.wikipedia.org/wiki/Minguo_calendar
 1487+ * http://en.wikipedia.org/wiki/Japanese_era_name
 1488+ *
 1489+ * @param $ts String: 14-character timestamp
 1490+ * @param $cName String: calender name
 1491+ * @return Array: converted year, month, day
 1492+ */
 1493+ private static function tsToYear( $ts, $cName ) {
 1494+ $gy = substr( $ts, 0, 4 );
 1495+ $gm = substr( $ts, 4, 2 );
 1496+ $gd = substr( $ts, 6, 2 );
 1497+
 1498+ if ( !strcmp( $cName, 'thai' ) ) {
 1499+ # Thai solar dates
 1500+ # Add 543 years to the Gregorian calendar
 1501+ # Months and days are identical
 1502+ $gy_offset = $gy + 543;
 1503+ } elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) {
 1504+ # Minguo dates
 1505+ # Deduct 1911 years from the Gregorian calendar
 1506+ # Months and days are identical
 1507+ $gy_offset = $gy - 1911;
 1508+ } elseif ( !strcmp( $cName, 'tenno' ) ) {
 1509+ # NengÅ� dates up to Meiji period
 1510+ # Deduct years from the Gregorian calendar
 1511+ # depending on the nengo periods
 1512+ # Months and days are identical
 1513+ if ( ( $gy < 1912 ) || ( ( $gy == 1912 ) && ( $gm < 7 ) ) || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) ) ) {
 1514+ # Meiji period
 1515+ $gy_gannen = $gy - 1868 + 1;
 1516+ $gy_offset = $gy_gannen;
 1517+ if ( $gy_gannen == 1 ) {
 1518+ $gy_offset = 'å…ƒ';
 1519+ }
 1520+ $gy_offset = '明治' . $gy_offset;
 1521+ } elseif (
 1522+ ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
 1523+ ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
 1524+ ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
 1525+ ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
 1526+ ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
 1527+ )
 1528+ {
 1529+ # TaishÅ� period
 1530+ $gy_gannen = $gy - 1912 + 1;
 1531+ $gy_offset = $gy_gannen;
 1532+ if ( $gy_gannen == 1 ) {
 1533+ $gy_offset = 'å…ƒ';
 1534+ }
 1535+ $gy_offset = '大正' . $gy_offset;
 1536+ } elseif (
 1537+ ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
 1538+ ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
 1539+ ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
 1540+ )
 1541+ {
 1542+ # ShÅ�wa period
 1543+ $gy_gannen = $gy - 1926 + 1;
 1544+ $gy_offset = $gy_gannen;
 1545+ if ( $gy_gannen == 1 ) {
 1546+ $gy_offset = 'å…ƒ';
 1547+ }
 1548+ $gy_offset = '昭和' . $gy_offset;
 1549+ } else {
 1550+ # Heisei period
 1551+ $gy_gannen = $gy - 1989 + 1;
 1552+ $gy_offset = $gy_gannen;
 1553+ if ( $gy_gannen == 1 ) {
 1554+ $gy_offset = 'å…ƒ';
 1555+ }
 1556+ $gy_offset = 'å¹³æˆ�' . $gy_offset;
 1557+ }
 1558+ } else {
 1559+ $gy_offset = $gy;
 1560+ }
 1561+
 1562+ return array( $gy_offset, $gm, $gd );
 1563+ }
 1564+
 1565+ /**
 1566+ * Roman number formatting up to 3000
 1567+ *
 1568+ * @param $num int
 1569+ *
 1570+ * @return string
 1571+ */
 1572+ static function romanNumeral( $num ) {
 1573+ static $table = array(
 1574+ array( '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ),
 1575+ array( '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ),
 1576+ array( '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ),
 1577+ array( '', 'M', 'MM', 'MMM' )
 1578+ );
 1579+
 1580+ $num = intval( $num );
 1581+ if ( $num > 3000 || $num <= 0 ) {
 1582+ return $num;
 1583+ }
 1584+
 1585+ $s = '';
 1586+ for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
 1587+ if ( $num >= $pow10 ) {
 1588+ $s .= $table[$i][floor( $num / $pow10 )];
 1589+ }
 1590+ $num = $num % $pow10;
 1591+ }
 1592+ return $s;
 1593+ }
 1594+
 1595+ /**
 1596+ * Hebrew Gematria number formatting up to 9999
 1597+ *
 1598+ * @param $num int
 1599+ *
 1600+ * @return string
 1601+ */
 1602+ static function hebrewNumeral( $num ) {
 1603+ static $table = array(
 1604+ array( '', '×�', 'ב', '×’', 'ד', '×”', 'ו', '×–', '×—', 'ט', '×™' ),
 1605+ array( '', '×™', '×›', 'ל', 'מ', '× ', 'ס', '×¢', 'פ', 'צ', '×§' ),
 1606+ array( '', '×§', 'ר', 'ש', 'ת', 'תק', 'תר', 'תש', 'תת', 'תתק', 'תתר' ),
 1607+ array( '', '×�', 'ב', '×’', 'ד', '×”', 'ו', '×–', '×—', 'ט', '×™' )
 1608+ );
 1609+
 1610+ $num = intval( $num );
 1611+ if ( $num > 9999 || $num <= 0 ) {
 1612+ return $num;
 1613+ }
 1614+
 1615+ $s = '';
 1616+ for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
 1617+ if ( $num >= $pow10 ) {
 1618+ if ( $num == 15 || $num == 16 ) {
 1619+ $s .= $table[0][9] . $table[0][$num - 9];
 1620+ $num = 0;
 1621+ } else {
 1622+ $s .= $table[$i][intval( ( $num / $pow10 ) )];
 1623+ if ( $pow10 == 1000 ) {
 1624+ $s .= "'";
 1625+ }
 1626+ }
 1627+ }
 1628+ $num = $num % $pow10;
 1629+ }
 1630+ if ( strlen( $s ) == 2 ) {
 1631+ $str = $s . "'";
 1632+ } else {
 1633+ $str = substr( $s, 0, strlen( $s ) - 2 ) . '"';
 1634+ $str .= substr( $s, strlen( $s ) - 2, 2 );
 1635+ }
 1636+ $start = substr( $str, 0, strlen( $str ) - 2 );
 1637+ $end = substr( $str, strlen( $str ) - 2 );
 1638+ switch( $end ) {
 1639+ case '×›':
 1640+ $str = $start . 'ך';
 1641+ break;
 1642+ case 'מ':
 1643+ $str = $start . '×�';
 1644+ break;
 1645+ case '× ':
 1646+ $str = $start . 'ן';
 1647+ break;
 1648+ case 'פ':
 1649+ $str = $start . '×£';
 1650+ break;
 1651+ case 'צ':
 1652+ $str = $start . '×¥';
 1653+ break;
 1654+ }
 1655+ return $str;
 1656+ }
 1657+
 1658+ /**
 1659+ * This is meant to be used by time(), date(), and timeanddate() to get
 1660+ * the date preference they're supposed to use, it should be used in
 1661+ * all children.
 1662+ *
 1663+ *<code>
 1664+ * function timeanddate([...], $format = true) {
 1665+ * $datePreference = $this->dateFormat($format);
 1666+ * [...]
 1667+ * }
 1668+ *</code>
 1669+ *
 1670+ * @param $usePrefs Mixed: if true, the user's preference is used
 1671+ * if false, the site/language default is used
 1672+ * if int/string, assumed to be a format.
 1673+ * @return string
 1674+ */
 1675+ function dateFormat( $usePrefs = true ) {
 1676+ global $wgUser;
 1677+
 1678+ if ( is_bool( $usePrefs ) ) {
 1679+ if ( $usePrefs ) {
 1680+ $datePreference = $wgUser->getDatePreference();
 1681+ } else {
 1682+ $datePreference = (string)User::getDefaultOption( 'date' );
 1683+ }
 1684+ } else {
 1685+ $datePreference = (string)$usePrefs;
 1686+ }
 1687+
 1688+ // return int
 1689+ if ( $datePreference == '' ) {
 1690+ return 'default';
 1691+ }
 1692+
 1693+ return $datePreference;
 1694+ }
 1695+
 1696+ /**
 1697+ * Get a format string for a given type and preference
 1698+ * @param $type string May be date, time or both
 1699+ * @param $pref string The format name as it appears in Messages*.php
 1700+ *
 1701+ * @return string
 1702+ */
 1703+ function getDateFormatString( $type, $pref ) {
 1704+ if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
 1705+ if ( $pref == 'default' ) {
 1706+ $pref = $this->getDefaultDateFormat();
 1707+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1708+ } else {
 1709+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1710+ if ( is_null( $df ) ) {
 1711+ $pref = $this->getDefaultDateFormat();
 1712+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1713+ }
 1714+ }
 1715+ $this->dateFormatStrings[$type][$pref] = $df;
 1716+ }
 1717+ return $this->dateFormatStrings[$type][$pref];
 1718+ }
 1719+
 1720+ /**
 1721+ * @param $ts Mixed: the time format which needs to be turned into a
 1722+ * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
 1723+ * @param $adj Bool: whether to adjust the time output according to the
 1724+ * user configured offset ($timecorrection)
 1725+ * @param $format Mixed: true to use user's date format preference
 1726+ * @param $timecorrection String|bool the time offset as returned by
 1727+ * validateTimeZone() in Special:Preferences
 1728+ * @return string
 1729+ */
 1730+ function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
 1731+ $ts = wfTimestamp( TS_MW, $ts );
 1732+ if ( $adj ) {
 1733+ $ts = $this->userAdjust( $ts, $timecorrection );
 1734+ }
 1735+ $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
 1736+ return $this->sprintfDate( $df, $ts );
 1737+ }
 1738+
 1739+ /**
 1740+ * @param $ts Mixed: the time format which needs to be turned into a
 1741+ * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
 1742+ * @param $adj Bool: whether to adjust the time output according to the
 1743+ * user configured offset ($timecorrection)
 1744+ * @param $format Mixed: true to use user's date format preference
 1745+ * @param $timecorrection String|bool the time offset as returned by
 1746+ * validateTimeZone() in Special:Preferences
 1747+ * @return string
 1748+ */
 1749+ function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
 1750+ $ts = wfTimestamp( TS_MW, $ts );
 1751+ if ( $adj ) {
 1752+ $ts = $this->userAdjust( $ts, $timecorrection );
 1753+ }
 1754+ $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
 1755+ return $this->sprintfDate( $df, $ts );
 1756+ }
 1757+
 1758+ /**
 1759+ * @param $ts Mixed: the time format which needs to be turned into a
 1760+ * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
 1761+ * @param $adj Bool: whether to adjust the time output according to the
 1762+ * user configured offset ($timecorrection)
 1763+ * @param $format Mixed: what format to return, if it's false output the
 1764+ * default one (default true)
 1765+ * @param $timecorrection String|bool the time offset as returned by
 1766+ * validateTimeZone() in Special:Preferences
 1767+ * @return string
 1768+ */
 1769+ function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
 1770+ $ts = wfTimestamp( TS_MW, $ts );
 1771+ if ( $adj ) {
 1772+ $ts = $this->userAdjust( $ts, $timecorrection );
 1773+ }
 1774+ $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
 1775+ return $this->sprintfDate( $df, $ts );
 1776+ }
 1777+
 1778+ /**
 1779+ * @param $key string
 1780+ * @return array|null
 1781+ */
 1782+ function getMessage( $key ) {
 1783+ return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
 1784+ }
 1785+
 1786+ /**
 1787+ * @return array
 1788+ */
 1789+ function getAllMessages() {
 1790+ return self::$dataCache->getItem( $this->mCode, 'messages' );
 1791+ }
 1792+
 1793+ /**
 1794+ * @param $in
 1795+ * @param $out
 1796+ * @param $string
 1797+ * @return string
 1798+ */
 1799+ function iconv( $in, $out, $string ) {
 1800+ # This is a wrapper for iconv in all languages except esperanto,
 1801+ # which does some nasty x-conversions beforehand
 1802+
 1803+ # Even with //IGNORE iconv can whine about illegal characters in
 1804+ # *input* string. We just ignore those too.
 1805+ # REF: http://bugs.php.net/bug.php?id=37166
 1806+ # REF: https://bugzilla.wikimedia.org/show_bug.cgi?id=16885
 1807+ wfSuppressWarnings();
 1808+ $text = iconv( $in, $out . '//IGNORE', $string );
 1809+ wfRestoreWarnings();
 1810+ return $text;
 1811+ }
 1812+
 1813+ // callback functions for uc(), lc(), ucwords(), ucwordbreaks()
 1814+
 1815+ /**
 1816+ * @param $matches array
 1817+ * @return mixed|string
 1818+ */
 1819+ function ucwordbreaksCallbackAscii( $matches ) {
 1820+ return $this->ucfirst( $matches[1] );
 1821+ }
 1822+
 1823+ /**
 1824+ * @param $matches array
 1825+ * @return string
 1826+ */
 1827+ function ucwordbreaksCallbackMB( $matches ) {
 1828+ return mb_strtoupper( $matches[0] );
 1829+ }
 1830+
 1831+ /**
 1832+ * @param $matches array
 1833+ * @return string
 1834+ */
 1835+ function ucCallback( $matches ) {
 1836+ list( $wikiUpperChars ) = self::getCaseMaps();
 1837+ return strtr( $matches[1], $wikiUpperChars );
 1838+ }
 1839+
 1840+ /**
 1841+ * @param $matches array
 1842+ * @return string
 1843+ */
 1844+ function lcCallback( $matches ) {
 1845+ list( , $wikiLowerChars ) = self::getCaseMaps();
 1846+ return strtr( $matches[1], $wikiLowerChars );
 1847+ }
 1848+
 1849+ /**
 1850+ * @param $matches array
 1851+ * @return string
 1852+ */
 1853+ function ucwordsCallbackMB( $matches ) {
 1854+ return mb_strtoupper( $matches[0] );
 1855+ }
 1856+
 1857+ /**
 1858+ * @param $matches array
 1859+ * @return string
 1860+ */
 1861+ function ucwordsCallbackWiki( $matches ) {
 1862+ list( $wikiUpperChars ) = self::getCaseMaps();
 1863+ return strtr( $matches[0], $wikiUpperChars );
 1864+ }
 1865+
 1866+ /**
 1867+ * Make a string's first character uppercase
 1868+ *
 1869+ * @param $str string
 1870+ *
 1871+ * @return string
 1872+ */
 1873+ function ucfirst( $str ) {
 1874+ $o = ord( $str );
 1875+ if ( $o < 96 ) { // if already uppercase...
 1876+ return $str;
 1877+ } elseif ( $o < 128 ) {
 1878+ return ucfirst( $str ); // use PHP's ucfirst()
 1879+ } else {
 1880+ // fall back to more complex logic in case of multibyte strings
 1881+ return $this->uc( $str, true );
 1882+ }
 1883+ }
 1884+
 1885+ /**
 1886+ * Convert a string to uppercase
 1887+ *
 1888+ * @param $str string
 1889+ * @param $first bool
 1890+ *
 1891+ * @return string
 1892+ */
 1893+ function uc( $str, $first = false ) {
 1894+ if ( function_exists( 'mb_strtoupper' ) ) {
 1895+ if ( $first ) {
 1896+ if ( $this->isMultibyte( $str ) ) {
 1897+ return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
 1898+ } else {
 1899+ return ucfirst( $str );
 1900+ }
 1901+ } else {
 1902+ return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
 1903+ }
 1904+ } else {
 1905+ if ( $this->isMultibyte( $str ) ) {
 1906+ $x = $first ? '^' : '';
 1907+ return preg_replace_callback(
 1908+ "/$x([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
 1909+ array( $this, 'ucCallback' ),
 1910+ $str
 1911+ );
 1912+ } else {
 1913+ return $first ? ucfirst( $str ) : strtoupper( $str );
 1914+ }
 1915+ }
 1916+ }
 1917+
 1918+ /**
 1919+ * @param $str string
 1920+ * @return mixed|string
 1921+ */
 1922+ function lcfirst( $str ) {
 1923+ $o = ord( $str );
 1924+ if ( !$o ) {
 1925+ return strval( $str );
 1926+ } elseif ( $o >= 128 ) {
 1927+ return $this->lc( $str, true );
 1928+ } elseif ( $o > 96 ) {
 1929+ return $str;
 1930+ } else {
 1931+ $str[0] = strtolower( $str[0] );
 1932+ return $str;
 1933+ }
 1934+ }
 1935+
 1936+ /**
 1937+ * @param $str string
 1938+ * @param $first bool
 1939+ * @return mixed|string
 1940+ */
 1941+ function lc( $str, $first = false ) {
 1942+ if ( function_exists( 'mb_strtolower' ) ) {
 1943+ if ( $first ) {
 1944+ if ( $this->isMultibyte( $str ) ) {
 1945+ return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
 1946+ } else {
 1947+ return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
 1948+ }
 1949+ } else {
 1950+ return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
 1951+ }
 1952+ } else {
 1953+ if ( $this->isMultibyte( $str ) ) {
 1954+ $x = $first ? '^' : '';
 1955+ return preg_replace_callback(
 1956+ "/$x([A-Z]|[\\xc0-\\xff][\\x80-\\xbf]*)/",
 1957+ array( $this, 'lcCallback' ),
 1958+ $str
 1959+ );
 1960+ } else {
 1961+ return $first ? strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) : strtolower( $str );
 1962+ }
 1963+ }
 1964+ }
 1965+
 1966+ /**
 1967+ * @param $str string
 1968+ * @return bool
 1969+ */
 1970+ function isMultibyte( $str ) {
 1971+ return (bool)preg_match( '/[\x80-\xff]/', $str );
 1972+ }
 1973+
 1974+ /**
 1975+ * @param $str string
 1976+ * @return mixed|string
 1977+ */
 1978+ function ucwords( $str ) {
 1979+ if ( $this->isMultibyte( $str ) ) {
 1980+ $str = $this->lc( $str );
 1981+
 1982+ // regexp to find first letter in each word (i.e. after each space)
 1983+ $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
 1984+
 1985+ // function to use to capitalize a single char
 1986+ if ( function_exists( 'mb_strtoupper' ) ) {
 1987+ return preg_replace_callback(
 1988+ $replaceRegexp,
 1989+ array( $this, 'ucwordsCallbackMB' ),
 1990+ $str
 1991+ );
 1992+ } else {
 1993+ return preg_replace_callback(
 1994+ $replaceRegexp,
 1995+ array( $this, 'ucwordsCallbackWiki' ),
 1996+ $str
 1997+ );
 1998+ }
 1999+ } else {
 2000+ return ucwords( strtolower( $str ) );
 2001+ }
 2002+ }
 2003+
 2004+ /**
 2005+ * capitalize words at word breaks
 2006+ *
 2007+ * @param $str string
 2008+ * @return mixed
 2009+ */
 2010+ function ucwordbreaks( $str ) {
 2011+ if ( $this->isMultibyte( $str ) ) {
 2012+ $str = $this->lc( $str );
 2013+
 2014+ // since \b doesn't work for UTF-8, we explicitely define word break chars
 2015+ $breaks = "[ \-\(\)\}\{\.,\?!]";
 2016+
 2017+ // find first letter after word break
 2018+ $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
 2019+
 2020+ if ( function_exists( 'mb_strtoupper' ) ) {
 2021+ return preg_replace_callback(
 2022+ $replaceRegexp,
 2023+ array( $this, 'ucwordbreaksCallbackMB' ),
 2024+ $str
 2025+ );
 2026+ } else {
 2027+ return preg_replace_callback(
 2028+ $replaceRegexp,
 2029+ array( $this, 'ucwordsCallbackWiki' ),
 2030+ $str
 2031+ );
 2032+ }
 2033+ } else {
 2034+ return preg_replace_callback(
 2035+ '/\b([\w\x80-\xff]+)\b/',
 2036+ array( $this, 'ucwordbreaksCallbackAscii' ),
 2037+ $str
 2038+ );
 2039+ }
 2040+ }
 2041+
 2042+ /**
 2043+ * Return a case-folded representation of $s
 2044+ *
 2045+ * This is a representation such that caseFold($s1)==caseFold($s2) if $s1
 2046+ * and $s2 are the same except for the case of their characters. It is not
 2047+ * necessary for the value returned to make sense when displayed.
 2048+ *
 2049+ * Do *not* perform any other normalisation in this function. If a caller
 2050+ * uses this function when it should be using a more general normalisation
 2051+ * function, then fix the caller.
 2052+ *
 2053+ * @param $s string
 2054+ *
 2055+ * @return string
 2056+ */
 2057+ function caseFold( $s ) {
 2058+ return $this->uc( $s );
 2059+ }
 2060+
 2061+ /**
 2062+ * @param $s string
 2063+ * @return string
 2064+ */
 2065+ function checkTitleEncoding( $s ) {
 2066+ if ( is_array( $s ) ) {
 2067+ wfDebugDieBacktrace( 'Given array to checkTitleEncoding.' );
 2068+ }
 2069+ # Check for non-UTF-8 URLs
 2070+ $ishigh = preg_match( '/[\x80-\xff]/', $s );
 2071+ if ( !$ishigh ) {
 2072+ return $s;
 2073+ }
 2074+
 2075+ $isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
 2076+ '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s );
 2077+ if ( $isutf8 ) {
 2078+ return $s;
 2079+ }
 2080+
 2081+ return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
 2082+ }
 2083+
 2084+ /**
 2085+ * @return array
 2086+ */
 2087+ function fallback8bitEncoding() {
 2088+ return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
 2089+ }
 2090+
 2091+ /**
 2092+ * Most writing systems use whitespace to break up words.
 2093+ * Some languages such as Chinese don't conventionally do this,
 2094+ * which requires special handling when breaking up words for
 2095+ * searching etc.
 2096+ *
 2097+ * @return bool
 2098+ */
 2099+ function hasWordBreaks() {
 2100+ return true;
 2101+ }
 2102+
 2103+ /**
 2104+ * Some languages such as Chinese require word segmentation,
 2105+ * Specify such segmentation when overridden in derived class.
 2106+ *
 2107+ * @param $string String
 2108+ * @return String
 2109+ */
 2110+ function segmentByWord( $string ) {
 2111+ return $string;
 2112+ }
 2113+
 2114+ /**
 2115+ * Some languages have special punctuation need to be normalized.
 2116+ * Make such changes here.
 2117+ *
 2118+ * @param $string String
 2119+ * @return String
 2120+ */
 2121+ function normalizeForSearch( $string ) {
 2122+ return self::convertDoubleWidth( $string );
 2123+ }
 2124+
 2125+ /**
 2126+ * convert double-width roman characters to single-width.
 2127+ * range: ff00-ff5f ~= 0020-007f
 2128+ *
 2129+ * @param $string string
 2130+ *
 2131+ * @return string
 2132+ */
 2133+ protected static function convertDoubleWidth( $string ) {
 2134+ static $full = null;
 2135+ static $half = null;
 2136+
 2137+ if ( $full === null ) {
 2138+ $fullWidth = "ï¼�123456789ABCDEFGHIJKLMNOPQRSTUVWXY