29use CLDRPluralRuleParser\Evaluator;
30use Wikimedia\Assert\Assert;
97 'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
102 'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
106 'january',
'february',
'march',
'april',
'may_long',
'june',
107 'july',
'august',
'september',
'october',
'november',
111 'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
112 'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
116 'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
117 'sep',
'oct',
'nov',
'dec'
121 'iranian-calendar-m1',
'iranian-calendar-m2',
'iranian-calendar-m3',
122 'iranian-calendar-m4',
'iranian-calendar-m5',
'iranian-calendar-m6',
123 'iranian-calendar-m7',
'iranian-calendar-m8',
'iranian-calendar-m9',
124 'iranian-calendar-m10',
'iranian-calendar-m11',
'iranian-calendar-m12'
128 'hebrew-calendar-m1',
'hebrew-calendar-m2',
'hebrew-calendar-m3',
129 'hebrew-calendar-m4',
'hebrew-calendar-m5',
'hebrew-calendar-m6',
130 'hebrew-calendar-m7',
'hebrew-calendar-m8',
'hebrew-calendar-m9',
131 'hebrew-calendar-m10',
'hebrew-calendar-m11',
'hebrew-calendar-m12',
132 'hebrew-calendar-m6a',
'hebrew-calendar-m6b'
136 'hebrew-calendar-m1-gen',
'hebrew-calendar-m2-gen',
'hebrew-calendar-m3-gen',
137 'hebrew-calendar-m4-gen',
'hebrew-calendar-m5-gen',
'hebrew-calendar-m6-gen',
138 'hebrew-calendar-m7-gen',
'hebrew-calendar-m8-gen',
'hebrew-calendar-m9-gen',
139 'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
140 'hebrew-calendar-m6a-gen',
'hebrew-calendar-m6b-gen'
144 'hijri-calendar-m1',
'hijri-calendar-m2',
'hijri-calendar-m3',
145 'hijri-calendar-m4',
'hijri-calendar-m5',
'hijri-calendar-m6',
146 'hijri-calendar-m7',
'hijri-calendar-m8',
'hijri-calendar-m9',
147 'hijri-calendar-m10',
'hijri-calendar-m11',
'hijri-calendar-m12'
154 public static $durationIntervals = [
155 'millennia' => 31556952000,
156 'centuries' => 3155695200,
157 'decades' => 315569520,
172 private static $fallbackLanguageCache = [];
189 private static $lre =
"\u{202A}";
190 private static $rle =
"\u{202B}";
191 private static $pdf =
"\u{202C}";
206 private static $strongDirRegex =
'/(?:([\x{41}-\x{5a}\x{61}-\x{7a}\x{aa}\x{b5}\x{ba}\x{c0}-\x{d6}\x{d8}-\x{f6}\x{f8}-\x{2b8}\x{2bb}-\x{2c1}\x{2d0}\x{2d1}\x{2e0}-\x{2e4}\x{2ee}\x{370}-\x{373}\x{376}\x{377}\x{37a}-\x{37d}\x{37f}\x{386}\x{388}-\x{38a}\x{38c}\x{38e}-\x{3a1}\x{3a3}-\x{3f5}\x{3f7}-\x{482}\x{48a}-\x{52f}\x{531}-\x{556}\x{559}-\x{55f}\x{561}-\x{587}\x{589}\x{903}-\x{939}\x{93b}\x{93d}-\x{940}\x{949}-\x{94c}\x{94e}-\x{950}\x{958}-\x{961}\x{964}-\x{980}\x{982}\x{983}\x{985}-\x{98c}\x{98f}\x{990}\x{993}-\x{9a8}\x{9aa}-\x{9b0}\x{9b2}\x{9b6}-\x{9b9}\x{9bd}-\x{9c0}\x{9c7}\x{9c8}\x{9cb}\x{9cc}\x{9ce}\x{9d7}\x{9dc}\x{9dd}\x{9df}-\x{9e1}\x{9e6}-\x{9f1}\x{9f4}-\x{9fa}\x{a03}\x{a05}-\x{a0a}\x{a0f}\x{a10}\x{a13}-\x{a28}\x{a2a}-\x{a30}\x{a32}\x{a33}\x{a35}\x{a36}\x{a38}\x{a39}\x{a3e}-\x{a40}\x{a59}-\x{a5c}\x{a5e}\x{a66}-\x{a6f}\x{a72}-\x{a74}\x{a83}\x{a85}-\x{a8d}\x{a8f}-\x{a91}\x{a93}-\x{aa8}\x{aaa}-\x{ab0}\x{ab2}\x{ab3}\x{ab5}-\x{ab9}\x{abd}-\x{ac0}\x{ac9}\x{acb}\x{acc}\x{ad0}\x{ae0}\x{ae1}\x{ae6}-\x{af0}\x{af9}\x{b02}\x{b03}\x{b05}-\x{b0c}\x{b0f}\x{b10}\x{b13}-\x{b28}\x{b2a}-\x{b30}\x{b32}\x{b33}\x{b35}-\x{b39}\x{b3d}\x{b3e}\x{b40}\x{b47}\x{b48}\x{b4b}\x{b4c}\x{b57}\x{b5c}\x{b5d}\x{b5f}-\x{b61}\x{b66}-\x{b77}\x{b83}\x{b85}-\x{b8a}\x{b8e}-\x{b90}\x{b92}-\x{b95}\x{b99}\x{b9a}\x{b9c}\x{b9e}\x{b9f}\x{ba3}\x{ba4}\x{ba8}-\x{baa}\x{bae}-\x{bb9}\x{bbe}\x{bbf}\x{bc1}\x{bc2}\x{bc6}-\x{bc8}\x{bca}-\x{bcc}\x{bd0}\x{bd7}\x{be6}-\x{bf2}\x{c01}-\x{c03}\x{c05}-\x{c0c}\x{c0e}-\x{c10}\x{c12}-\x{c28}\x{c2a}-\x{c39}\x{c3d}\x{c41}-\x{c44}\x{c58}-\x{c5a}\x{c60}\x{c61}\x{c66}-\x{c6f}\x{c7f}\x{c82}\x{c83}\x{c85}-\x{c8c}\x{c8e}-\x{c90}\x{c92}-\x{ca8}\x{caa}-\x{cb3}\x{cb5}-\x{cb9}\x{cbd}-\x{cc4}\x{cc6}-\x{cc8}\x{cca}\x{ccb}\x{cd5}\x{cd6}\x{cde}\x{ce0}\x{ce1}\x{ce6}-\x{cef}\x{cf1}\x{cf2}\x{d02}\x{d03}\x{d05}-\x{d0c}\x{d0e}-\x{d10}\x{d12}-\x{d3a}\x{d3d}-\x{d40}\x{d46}-\x{d48}\x{d4a}-\x{d4c}\x{d4e}\x{d57}\x{d5f}-\x{d61}\x{d66}-\x{d75}\x{d79}-\x{d7f}\x{d82}\x{d83}\x{d85}-\x{d96}\x{d9a}-\x{db1}\x{db3}-\x{dbb}\x{dbd}\x{dc0}-\x{dc6}\x{dcf}-\x{dd1}\x{dd8}-\x{ddf}\x{de6}-\x{def}\x{df2}-\x{df4}\x{e01}-\x{e30}\x{e32}\x{e33}\x{e40}-\x{e46}\x{e4f}-\x{e5b}\x{e81}\x{e82}\x{e84}\x{e87}\x{e88}\x{e8a}\x{e8d}\x{e94}-\x{e97}\x{e99}-\x{e9f}\x{ea1}-\x{ea3}\x{ea5}\x{ea7}\x{eaa}\x{eab}\x{ead}-\x{eb0}\x{eb2}\x{eb3}\x{ebd}\x{ec0}-\x{ec4}\x{ec6}\x{ed0}-\x{ed9}\x{edc}-\x{edf}\x{f00}-\x{f17}\x{f1a}-\x{f34}\x{f36}\x{f38}\x{f3e}-\x{f47}\x{f49}-\x{f6c}\x{f7f}\x{f85}\x{f88}-\x{f8c}\x{fbe}-\x{fc5}\x{fc7}-\x{fcc}\x{fce}-\x{fda}\x{1000}-\x{102c}\x{1031}\x{1038}\x{103b}\x{103c}\x{103f}-\x{1057}\x{105a}-\x{105d}\x{1061}-\x{1070}\x{1075}-\x{1081}\x{1083}\x{1084}\x{1087}-\x{108c}\x{108e}-\x{109c}\x{109e}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1360}-\x{137c}\x{1380}-\x{138f}\x{13a0}-\x{13f5}\x{13f8}-\x{13fd}\x{1401}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16f8}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1735}\x{1736}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17b6}\x{17be}-\x{17c5}\x{17c7}\x{17c8}\x{17d4}-\x{17da}\x{17dc}\x{17e0}-\x{17e9}\x{1810}-\x{1819}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191e}\x{1923}-\x{1926}\x{1929}-\x{192b}\x{1930}\x{1931}\x{1933}-\x{1938}\x{1946}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19b0}-\x{19c9}\x{19d0}-\x{19da}\x{1a00}-\x{1a16}\x{1a19}\x{1a1a}\x{1a1e}-\x{1a55}\x{1a57}\x{1a61}\x{1a63}\x{1a64}\x{1a6d}-\x{1a72}\x{1a80}-\x{1a89}\x{1a90}-\x{1a99}\x{1aa0}-\x{1aad}\x{1b04}-\x{1b33}\x{1b35}\x{1b3b}\x{1b3d}-\x{1b41}\x{1b43}-\x{1b4b}\x{1b50}-\x{1b6a}\x{1b74}-\x{1b7c}\x{1b82}-\x{1ba1}\x{1ba6}\x{1ba7}\x{1baa}\x{1bae}-\x{1be5}\x{1be7}\x{1bea}-\x{1bec}\x{1bee}\x{1bf2}\x{1bf3}\x{1bfc}-\x{1c2b}\x{1c34}\x{1c35}\x{1c3b}-\x{1c49}\x{1c4d}-\x{1c7f}\x{1cc0}-\x{1cc7}\x{1cd3}\x{1ce1}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf3}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{200e}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{214f}\x{2160}-\x{2188}\x{2336}-\x{237a}\x{2395}\x{249c}-\x{24e9}\x{26ac}\x{2800}-\x{28ff}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d70}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{302e}\x{302f}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{3190}-\x{31ba}\x{31f0}-\x{321c}\x{3220}-\x{324f}\x{3260}-\x{327b}\x{327f}-\x{32b0}\x{32c0}-\x{32cb}\x{32d0}-\x{32fe}\x{3300}-\x{3376}\x{337b}-\x{33dd}\x{33e0}-\x{33fe}\x{3400}-\x{4db5}\x{4e00}-\x{9fd5}\x{a000}-\x{a48c}\x{a4d0}-\x{a60c}\x{a610}-\x{a62b}\x{a640}-\x{a66e}\x{a680}-\x{a69d}\x{a6a0}-\x{a6ef}\x{a6f2}-\x{a6f7}\x{a722}-\x{a787}\x{a789}-\x{a7ad}\x{a7b0}-\x{a7b7}\x{a7f7}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a824}\x{a827}\x{a830}-\x{a837}\x{a840}-\x{a873}\x{a880}-\x{a8c3}\x{a8ce}-\x{a8d9}\x{a8f2}-\x{a8fd}\x{a900}-\x{a925}\x{a92e}-\x{a946}\x{a952}\x{a953}\x{a95f}-\x{a97c}\x{a983}-\x{a9b2}\x{a9b4}\x{a9b5}\x{a9ba}\x{a9bb}\x{a9bd}-\x{a9cd}\x{a9cf}-\x{a9d9}\x{a9de}-\x{a9e4}\x{a9e6}-\x{a9fe}\x{aa00}-\x{aa28}\x{aa2f}\x{aa30}\x{aa33}\x{aa34}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa4d}\x{aa50}-\x{aa59}\x{aa5c}-\x{aa7b}\x{aa7d}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aaeb}\x{aaee}-\x{aaf5}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{ab30}-\x{ab65}\x{ab70}-\x{abe4}\x{abe6}\x{abe7}\x{abe9}-\x{abec}\x{abf0}-\x{abf9}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{e000}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}\x{10000}-\x{1000b}\x{1000d}-\x{10026}\x{10028}-\x{1003a}\x{1003c}\x{1003d}\x{1003f}-\x{1004d}\x{10050}-\x{1005d}\x{10080}-\x{100fa}\x{10100}\x{10102}\x{10107}-\x{10133}\x{10137}-\x{1013f}\x{101d0}-\x{101fc}\x{10280}-\x{1029c}\x{102a0}-\x{102d0}\x{10300}-\x{10323}\x{10330}-\x{1034a}\x{10350}-\x{10375}\x{10380}-\x{1039d}\x{1039f}-\x{103c3}\x{103c8}-\x{103d5}\x{10400}-\x{1049d}\x{104a0}-\x{104a9}\x{10500}-\x{10527}\x{10530}-\x{10563}\x{1056f}\x{10600}-\x{10736}\x{10740}-\x{10755}\x{10760}-\x{10767}\x{11000}\x{11002}-\x{11037}\x{11047}-\x{1104d}\x{11066}-\x{1106f}\x{11082}-\x{110b2}\x{110b7}\x{110b8}\x{110bb}-\x{110c1}\x{110d0}-\x{110e8}\x{110f0}-\x{110f9}\x{11103}-\x{11126}\x{1112c}\x{11136}-\x{11143}\x{11150}-\x{11172}\x{11174}-\x{11176}\x{11182}-\x{111b5}\x{111bf}-\x{111c9}\x{111cd}\x{111d0}-\x{111df}\x{111e1}-\x{111f4}\x{11200}-\x{11211}\x{11213}-\x{1122e}\x{11232}\x{11233}\x{11235}\x{11238}-\x{1123d}\x{11280}-\x{11286}\x{11288}\x{1128a}-\x{1128d}\x{1128f}-\x{1129d}\x{1129f}-\x{112a9}\x{112b0}-\x{112de}\x{112e0}-\x{112e2}\x{112f0}-\x{112f9}\x{11302}\x{11303}\x{11305}-\x{1130c}\x{1130f}\x{11310}\x{11313}-\x{11328}\x{1132a}-\x{11330}\x{11332}\x{11333}\x{11335}-\x{11339}\x{1133d}-\x{1133f}\x{11341}-\x{11344}\x{11347}\x{11348}\x{1134b}-\x{1134d}\x{11350}\x{11357}\x{1135d}-\x{11363}\x{11480}-\x{114b2}\x{114b9}\x{114bb}-\x{114be}\x{114c1}\x{114c4}-\x{114c7}\x{114d0}-\x{114d9}\x{11580}-\x{115b1}\x{115b8}-\x{115bb}\x{115be}\x{115c1}-\x{115db}\x{11600}-\x{11632}\x{1163b}\x{1163c}\x{1163e}\x{11641}-\x{11644}\x{11650}-\x{11659}\x{11680}-\x{116aa}\x{116ac}\x{116ae}\x{116af}\x{116b6}\x{116c0}-\x{116c9}\x{11700}-\x{11719}\x{11720}\x{11721}\x{11726}\x{11730}-\x{1173f}\x{118a0}-\x{118f2}\x{118ff}\x{11ac0}-\x{11af8}\x{12000}-\x{12399}\x{12400}-\x{1246e}\x{12470}-\x{12474}\x{12480}-\x{12543}\x{13000}-\x{1342e}\x{14400}-\x{14646}\x{16800}-\x{16a38}\x{16a40}-\x{16a5e}\x{16a60}-\x{16a69}\x{16a6e}\x{16a6f}\x{16ad0}-\x{16aed}\x{16af5}\x{16b00}-\x{16b2f}\x{16b37}-\x{16b45}\x{16b50}-\x{16b59}\x{16b5b}-\x{16b61}\x{16b63}-\x{16b77}\x{16b7d}-\x{16b8f}\x{16f00}-\x{16f44}\x{16f50}-\x{16f7e}\x{16f93}-\x{16f9f}\x{1b000}\x{1b001}\x{1bc00}-\x{1bc6a}\x{1bc70}-\x{1bc7c}\x{1bc80}-\x{1bc88}\x{1bc90}-\x{1bc99}\x{1bc9c}\x{1bc9f}\x{1d000}-\x{1d0f5}\x{1d100}-\x{1d126}\x{1d129}-\x{1d166}\x{1d16a}-\x{1d172}\x{1d183}\x{1d184}\x{1d18c}-\x{1d1a9}\x{1d1ae}-\x{1d1e8}\x{1d360}-\x{1d371}\x{1d400}-\x{1d454}\x{1d456}-\x{1d49c}\x{1d49e}\x{1d49f}\x{1d4a2}\x{1d4a5}\x{1d4a6}\x{1d4a9}-\x{1d4ac}\x{1d4ae}-\x{1d4b9}\x{1d4bb}\x{1d4bd}-\x{1d4c3}\x{1d4c5}-\x{1d505}\x{1d507}-\x{1d50a}\x{1d50d}-\x{1d514}\x{1d516}-\x{1d51c}\x{1d51e}-\x{1d539}\x{1d53b}-\x{1d53e}\x{1d540}-\x{1d544}\x{1d546}\x{1d54a}-\x{1d550}\x{1d552}-\x{1d6a5}\x{1d6a8}-\x{1d6da}\x{1d6dc}-\x{1d714}\x{1d716}-\x{1d74e}\x{1d750}-\x{1d788}\x{1d78a}-\x{1d7c2}\x{1d7c4}-\x{1d7cb}\x{1d800}-\x{1d9ff}\x{1da37}-\x{1da3a}\x{1da6d}-\x{1da74}\x{1da76}-\x{1da83}\x{1da85}-\x{1da8b}\x{1f110}-\x{1f12e}\x{1f130}-\x{1f169}\x{1f170}-\x{1f19a}\x{1f1e6}-\x{1f202}\x{1f210}-\x{1f23a}\x{1f240}-\x{1f248}\x{1f250}\x{1f251}\x{20000}-\x{2a6d6}\x{2a700}-\x{2b734}\x{2b740}-\x{2b81d}\x{2b820}-\x{2cea1}\x{2f800}-\x{2fa1d}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}])|([\x{590}\x{5be}\x{5c0}\x{5c3}\x{5c6}\x{5c8}-\x{5ff}\x{7c0}-\x{7ea}\x{7f4}\x{7f5}\x{7fa}-\x{815}\x{81a}\x{824}\x{828}\x{82e}-\x{858}\x{85c}-\x{89f}\x{200f}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb4f}\x{10800}-\x{1091e}\x{10920}-\x{10a00}\x{10a04}\x{10a07}-\x{10a0b}\x{10a10}-\x{10a37}\x{10a3b}-\x{10a3e}\x{10a40}-\x{10ae4}\x{10ae7}-\x{10b38}\x{10b40}-\x{10e5f}\x{10e7f}-\x{10fff}\x{1e800}-\x{1e8cf}\x{1e8d7}-\x{1edff}\x{1ef00}-\x{1efff}\x{608}\x{60b}\x{60d}\x{61b}-\x{64a}\x{66d}-\x{66f}\x{671}-\x{6d5}\x{6e5}\x{6e6}\x{6ee}\x{6ef}\x{6fa}-\x{710}\x{712}-\x{72f}\x{74b}-\x{7a5}\x{7b1}-\x{7bf}\x{8a0}-\x{8e2}\x{fb50}-\x{fd3d}\x{fd40}-\x{fdcf}\x{fdf0}-\x{fdfc}\x{fdfe}\x{fdff}\x{fe70}-\x{fefe}\x{1ee00}-\x{1eeef}\x{1eef2}-\x{1eeff}]))/u';
223 $langObj = self::$mLangObjCache[
$code] ?? self::newFromCode(
$code );
226 self::$mLangObjCache = array_merge( [
$code => $langObj ], self::$mLangObjCache );
241 if ( !self::isValidCode(
$code ) ) {
242 throw new MWException(
"Invalid language code \"$code\"" );
245 if ( !self::isValidBuiltInCode(
$code ) ) {
256 if ( class_exists( $class ) && is_a( $class,
'Language',
true ) ) {
262 $fallbacks = self::getFallbacksFor(
$code );
263 foreach ( $fallbacks
as $fallbackCode ) {
264 if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
265 throw new MWException(
"Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
268 $class = self::classFromCode( $fallbackCode );
269 if ( class_exists( $class ) ) {
276 throw new MWException(
"Invalid fallback sequence for language '$code'" );
285 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
286 throw new MWException( __METHOD__ .
' must not be used outside tests' );
288 self::$dataCache =
null;
290 self::getLocalisationCache();
291 self::$mLangObjCache = [];
292 self::$fallbackLanguageCache = [];
293 self::$grammarTransformations =
null;
294 self::$languageNameCache =
null;
306 if ( !self::isValidBuiltInCode(
$code ) ) {
310 if (
$code ===
'qqq' ) {
314 return is_readable( self::getMessagesFileName(
$code ) ) ||
315 is_readable( self::getJsonMessagesFileName(
$code ) );
336 $alphanum =
'[a-z0-9]';
337 $x =
'x'; #
private use singleton
338 $singleton =
'[a-wy-z]'; #
other singleton
339 $s = $lenient ?
'[-_]' :
'-';
341 $language =
"$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
342 $script =
"$alpha{4}"; # ISO 15924
343 $region =
"(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2
or UN M.49
344 $variant =
"(?:$alphanum{5,8}|$digit$alphanum{3})";
345 $extension =
"$singleton(?:$s$alphanum{2,8})+";
346 $privateUse =
"$x(?:$s$alphanum{1,8})+";
348 # Define certain grandfathered codes, since otherwise the regex is pretty useless.
349 # Since these are limited, this is safe even later changes to the registry --
350 # the only oddity is that it might change the type of the tag, and thus
351 # the results from the capturing groups.
354 $grandfathered =
"en{$s}GB{$s}oed"
355 .
"|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
356 .
"|no{$s}(?:bok|nyn)"
357 .
"|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
358 .
"|zh{$s}min{$s}nan";
360 $variantList =
"$variant(?:$s$variant)*";
361 $extensionList =
"$extension(?:$s$extension)*";
363 $langtag =
"(?:($language)"
366 .
"(?:$s$variantList)?"
367 .
"(?:$s$extensionList)?"
368 .
"(?:$s$privateUse)?)";
370 # The final breakdown, with capturing groups for each of these components
371 # The variants, extensions, grandfathered, and private-use may have interior '-'
373 $root =
"^(?:$langtag|$privateUse|$grandfathered)$";
375 return (
bool)preg_match(
"/$root/", strtolower(
$code ) );
389 Assert::parameterType(
'string',
$code,
'$code' );
397 strcspn(
$code,
":/\\\000&<>'\"" ) === strlen(
$code )
413 Assert::parameterType(
'string',
$code,
'$code' );
415 return (
bool)preg_match(
'/^[a-z0-9-]{2,}$/',
$code );
429 if ( !self::isValidBuiltInCode( $tag ) ) {
434 || self::fetchLanguageName( $tag, $tag ) !==
''
448 if ( is_null( self::$dataCache ) ) {
453 return self::$dataCache;
459 if ( static::class ===
'Language' ) {
462 $this->mCode = str_replace(
'_',
'-', strtolower( substr( static::class, 8 ) ) );
464 self::getLocalisationCache();
472 unset( $this->
$name );
488 return self::getFallbacksFor( $this->mCode );
496 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
506 if ( is_null( $this->namespaceNames ) ) {
509 $validNamespaces = MWNamespace::getCanonicalNamespaces();
512 self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
513 $this->namespaceNames += $validNamespaces;
524 # Sometimes a language will be localised but not actually exist on this wiki.
525 foreach ( $this->namespaceNames
as $key => $text ) {
526 if ( !isset( $validNamespaces[$key] ) ) {
527 unset( $this->namespaceNames[$key] );
531 # The above mixing may leave namespaces out of canonical order.
532 # Re-order by namespace ID number...
533 ksort( $this->namespaceNames );
535 Hooks::run(
'LanguageGetNamespaces', [ &$this->namespaceNames ] );
547 $this->mNamespaceIds =
null;
554 $this->namespaceNames =
null;
555 $this->mNamespaceIds =
null;
556 $this->namespaceAliases =
null;
567 foreach ( $ns
as $k => $v ) {
568 $ns[$k] = strtr( $v,
'_',
' ' );
586 return $ns[$index] ??
false;
604 return strtr( $ns,
'_',
' ' );
619 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
621 return $ns[$index][$gender] ?? $this->
getNsText( $index );
641 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
642 return count( $aliases ) > 0;
655 $lctext = $this->
lc( $text );
657 return $ids[$lctext] ??
false;
664 if ( is_null( $this->namespaceAliases ) ) {
665 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
669 foreach ( $aliases
as $name => $index ) {
671 unset( $aliases[
$name] );
673 $aliases[
$name] = $index;
680 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
681 foreach ( $genders
as $index => $forms ) {
682 foreach ( $forms
as $alias ) {
683 $aliases[$alias] = $index;
687 # Also add converted namespace names as aliases, to avoid confusion.
688 $convertedNames = [];
690 if ( $variant === $this->mCode ) {
694 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
698 $this->namespaceAliases = $aliases + $convertedNames;
700 # Filter out aliases to namespaces that don't exist, e.g. from extensions
701 # that aren't loaded here but are included in the l10n cache.
702 # (array_intersect preserves keys from its first argument)
703 $this->namespaceAliases = array_intersect(
704 $this->namespaceAliases,
716 if ( is_null( $this->mNamespaceIds ) ) {
718 # Put namespace names and aliases into a hashtable.
719 # If this is too slow, then we should arrange it so that it is done
720 # before caching. The catch is that at pre-cache time, the above
721 # class-specific fixup hasn't been done.
722 $this->mNamespaceIds = [];
724 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
727 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
731 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
735 return $this->mNamespaceIds;
746 $lctext = $this->
lc( $text );
747 $ns = MWNamespace::getCanonicalIndex( $lctext );
748 if ( $ns !==
null ) {
752 return $ids[$lctext] ??
false;
763 $msg =
"variantname-$code";
764 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
769 return $name; #
if it's defined as a language name, show that
771 # otherwise, output the language code
779 public function getDatePreferences() {
780 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
786 function getDateFormats() {
787 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
793 public function getDefaultDateFormat() {
794 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
795 if ( $df === 'dmy
or mdy
' ) {
796 global $wgAmericanDates;
797 return $wgAmericanDates ? 'mdy
' : 'dmy
';
806 public function getDatePreferenceMigrationMap() {
807 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
813 public function getExtraUserToggles() {
814 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
821 function getUserToggle( $tog ) {
822 return $this->getMessageFromDB( "tog-$tog" );
836 public static function fetchLanguageNames( $inLanguage = self::AS_AUTONYMS, $include = 'mw
' ) {
837 $cacheKey = $inLanguage === self::AS_AUTONYMS ? 'null' : $inLanguage;
838 $cacheKey .= ":$include";
839 if ( self::$languageNameCache === null ) {
840 self::$languageNameCache = new HashBagOStuff( [ 'maxKeys
' => 20 ] );
843 $ret = self::$languageNameCache->get( $cacheKey );
845 $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
846 self::$languageNameCache->set( $cacheKey, $ret );
861 private static function fetchLanguageNamesUncached(
862 $inLanguage = self::AS_AUTONYMS,
865 global $wgExtraLanguageNames, $wgUsePigLatinVariant;
867 // If passed an invalid language code to use, fallback to en
868 if ( $inLanguage !== self::AS_AUTONYMS && !self::isValidCode( $inLanguage ) ) {
875 # TODO: also include when $inLanguage is null, when this code is more efficient
876 Hooks::run( 'LanguageGetTranslatedLanguageNames
', [ &$names, $inLanguage ] );
879 $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
880 if ( $wgUsePigLatinVariant ) {
881 // Pig Latin (for variant development)
882 $mwNames['en-x-piglatin
'] = 'Igpay Atinlay
';
885 foreach ( $mwNames as $mwCode => $mwName ) {
886 # - Prefer own MediaWiki native name when not using the hook
887 # - For other names just add if not added through the hook
888 if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
889 $names[$mwCode] = $mwName;
893 if ( $include === self::ALL ) {
899 $coreCodes = array_keys( $mwNames );
900 foreach ( $coreCodes as $coreCode ) {
901 $returnMw[$coreCode] = $names[$coreCode];
904 if ( $include === self::SUPPORTED ) {
906 # We do this using a foreach over the codes instead of a directory
907 # loop so that messages files in extensions will work correctly.
908 foreach ( $returnMw as $code => $value ) {
909 if ( is_readable( self::getMessagesFileName( $code ) )
910 || is_readable( self::getJsonMessagesFileName( $code ) )
912 $namesMwFile[$code] = $names[$code];
916 ksort( $namesMwFile );
921 # 'mw
' option; default if it's not one
of the
other two
options (all/mwfile)
935 $inLanguage = self::AS_AUTONYMS,
939 $array = self::fetchLanguageNames( $inLanguage, $include );
940 return !array_key_exists(
$code, $array ) ?
'' : $array[
$code];
950 return $this->
msg( $msg )->text();
959 protected function msg( $msg ) {
960 return wfMessage( $msg )->inLanguage( $this );
975 $monthNames = [
'' ];
976 for ( $i = 1; $i < 13; $i++ ) {
1002 $monthNames = [
'' ];
1003 for ( $i = 1; $i < 13; $i++ ) {
1030 return $this->
getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1038 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1046 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1054 return $this->
getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1066 if ( !$dateTimeObj ) {
1067 $dateTimeObj = DateTime::createFromFormat(
1068 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1071 return $dateTimeObj->format(
$code );
1143 public function sprintfDate( $format, $ts, DateTimeZone $zone =
null, &$ttl =
'unused' ) {
1148 $dateTimeObj =
false;
1157 $usedSecond =
false;
1158 $usedMinute =
false;
1165 $usedISOYear =
false;
1166 $usedIsLeapYear =
false;
1168 $usedHebrewMonth =
false;
1169 $usedIranianMonth =
false;
1170 $usedHijriMonth =
false;
1171 $usedHebrewYear =
false;
1172 $usedIranianYear =
false;
1173 $usedHijriYear =
false;
1174 $usedTennoYear =
false;
1176 if ( strlen( $ts ) !== 14 ) {
1177 throw new MWException( __METHOD__ .
": The timestamp $ts should have 14 characters" );
1180 if ( !ctype_digit( $ts ) ) {
1181 throw new MWException( __METHOD__ .
": The timestamp $ts should be a number" );
1184 $formatLength = strlen( $format );
1185 for ( $p = 0; $p < $formatLength; $p++ ) {
1187 $code = $format[$p];
1188 if (
$code ==
'x' && $p < $formatLength - 1 ) {
1189 $code .= $format[++$p];
1192 if ( (
$code ===
'xi'
1198 && $p < $formatLength - 1 ) {
1199 $code .= $format[++$p];
1210 $rawToggle = !$rawToggle;
1223 $usedHebrewMonth =
true;
1225 $hebrew = self::tsToHebrew( $ts );
1231 $num = substr( $ts, 6, 2 );
1236 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1241 $num = intval( substr( $ts, 6, 2 ) );
1246 $iranian = self::tsToIranian( $ts );
1253 $hijri = self::tsToHijri( $ts );
1260 $hebrew = self::tsToHebrew( $ts );
1267 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1275 $usedIranianMonth =
true;
1277 $iranian = self::tsToIranian( $ts );
1282 $usedHijriMonth =
true;
1284 $hijri = self::tsToHijri( $ts );
1289 $usedHebrewMonth =
true;
1291 $hebrew = self::tsToHebrew( $ts );
1297 $num = substr( $ts, 4, 2 );
1305 $num = intval( substr( $ts, 4, 2 ) );
1308 $usedIranianMonth =
true;
1310 $iranian = self::tsToIranian( $ts );
1315 $usedHijriMonth =
true;
1317 $hijri = self::tsToHijri( $ts );
1322 $usedHebrewMonth =
true;
1324 $hebrew = self::tsToHebrew( $ts );
1329 $usedHebrewMonth =
true;
1331 $hebrew = self::tsToHebrew( $ts );
1337 $num = substr( $ts, 0, 4 );
1340 $usedIranianYear =
true;
1342 $iranian = self::tsToIranian( $ts );
1347 $usedHijriYear =
true;
1349 $hijri = self::tsToHijri( $ts );
1354 $usedHebrewYear =
true;
1356 $hebrew = self::tsToHebrew( $ts );
1363 $thai = self::tsToYear( $ts,
'thai' );
1370 $minguo = self::tsToYear( $ts,
'minguo' );
1375 $usedTennoYear =
true;
1377 $tenno = self::tsToYear( $ts,
'tenno' );
1383 $num = substr( $ts, 2, 2 );
1386 $usedIranianYear =
true;
1388 $iranian = self::tsToIranian( $ts );
1390 $num = substr( $iranian[0], -2 );
1393 $usedIranianYear =
true;
1395 $iranian = self::tsToIranian( $ts );
1397 $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1400 $usedIranianYear =
true;
1402 $iranian = self::tsToIranian( $ts );
1408 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' :
'pm';
1412 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' :
'PM';
1416 $h = substr( $ts, 8, 2 );
1417 $num = $h % 12 ?: 12;
1421 $num = intval( substr( $ts, 8, 2 ) );
1425 $h = substr( $ts, 8, 2 );
1426 $num = sprintf(
'%02d', $h % 12 ?: 12 );
1430 $num = substr( $ts, 8, 2 );
1434 $num = substr( $ts, 10, 2 );
1438 $num = substr( $ts, 12, 2 );
1448 $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1454 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1458 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1462 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1465 $usedIsLeapYear =
true;
1466 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1469 $usedISOYear =
true;
1470 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1477 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1480 # Backslash escaping
1481 if ( $p < $formatLength - 1 ) {
1482 $s .= $format[++$p];
1489 if ( $p < $formatLength - 1 ) {
1490 $endQuote = strpos( $format,
'"', $p + 1 );
1491 if ( $endQuote ===
false ) {
1492 # No terminating quote, assume literal "
1495 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1499 # Quote at end of string, assume literal "
1506 if ( $num !==
false ) {
1507 if ( $rawToggle || $raw ) {
1510 } elseif ( $roman ) {
1511 $s .= self::romanNumeral( $num );
1513 } elseif ( $hebrewNum ) {
1514 $s .= self::hebrewNumeral( $num );
1522 if ( $ttl ===
'unused' ) {
1524 } elseif ( $usedSecond ) {
1526 } elseif ( $usedMinute ) {
1527 $ttl = 60 - substr( $ts, 12, 2 );
1528 } elseif ( $usedHour ) {
1529 $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1530 } elseif ( $usedAMPM ) {
1531 $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 -
1532 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1536 $usedIranianMonth ||
1545 $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1546 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1549 $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1550 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1553 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400 +
1554 $timeRemainingInDay;
1555 } elseif ( $usedISOYear ) {
1558 $lastWeekOfISOYear = DateTime::createFromFormat(
1560 substr( $ts, 0, 4 ) .
'1228',
1561 $zone ?:
new DateTimeZone(
'UTC' )
1563 $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'W' );
1564 $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
1565 $timeRemainingInWeek =
1566 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400
1567 + $timeRemainingInDay;
1568 $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
1573 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
't' ) -
1574 substr( $ts, 6, 2 ) ) * 86400
1575 + $timeRemainingInDay;
1576 } elseif ( $usedYear ) {
1578 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1579 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1580 + $timeRemainingInDay;
1581 } elseif ( $usedIsLeapYear ) {
1582 $year = substr( $ts, 0, 4 );
1583 $timeRemainingInYear =
1584 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1585 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1586 + $timeRemainingInDay;
1588 if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1590 $nextCandidate = $year - $mod + 4;
1591 if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1592 $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1593 $timeRemainingInYear;
1595 $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1596 $timeRemainingInYear;
1600 $possibleTtls[] = $timeRemainingInYear;
1604 if ( $possibleTtls ) {
1605 $ttl = min( $possibleTtls );
1612 private static $GREG_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
1613 private static $IRANIAN_DAYS = [ 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ];
1628 $gy = substr( $ts, 0, 4 ) - 1600;
1629 $gm = substr( $ts, 4, 2 ) - 1;
1630 $gd = substr( $ts, 6, 2 ) - 1;
1632 # Days passed from the beginning (including leap years)
1634 + floor( ( $gy + 3 ) / 4 )
1635 - floor( ( $gy + 99 ) / 100 )
1636 + floor( ( $gy + 399 ) / 400 );
1639 for ( $i = 0; $i < $gm; $i++ ) {
1640 $gDayNo += self::$GREG_DAYS[$i];
1644 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1649 $gDayNo += (int)$gd;
1651 $jDayNo = $gDayNo - 79;
1653 $jNp = floor( $jDayNo / 12053 );
1656 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1659 if ( $jDayNo >= 366 ) {
1660 $jy += floor( ( $jDayNo - 1 ) / 365 );
1661 $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1666 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1667 $jDayNo -= self::$IRANIAN_DAYS[$i];
1673 return [ $jy, $jm, $jd, $jz ];
1688 $year = substr( $ts, 0, 4 );
1689 $month = substr( $ts, 4, 2 );
1690 $day = substr( $ts, 6, 2 );
1698 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1699 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1701 $zjd = (int)( ( 1461 * ( $zy + 4800 + (
int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1702 (
int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1703 (
int)( ( 3 * (int)( ( ( $zy + 4900 + (
int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1706 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (
int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1707 (
int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1710 $zl = $zjd - 1948440 + 10632;
1711 $zn = (int)( ( $zl - 1 ) / 10631 );
1712 $zl = $zl - 10631 * $zn + 354;
1713 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
1714 ( (int)( $zl / 5670 ) ) * ( (
int)( ( 43 * $zl ) / 15238 ) );
1715 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
1716 ( (int)( $zj / 16 ) ) * ( (
int)( ( 15238 * $zj ) / 43 ) ) + 29;
1717 $zm = (int)( ( 24 * $zl ) / 709 );
1718 $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1719 $zy = 30 * $zn + $zj - 30;
1721 return [ $zy, $zm, $zd ];
1741 $year = substr( $ts, 0, 4 );
1742 $month = substr( $ts, 4, 2 );
1743 $day = substr( $ts, 6, 2 );
1745 # Calculate Hebrew year
1746 $hebrewYear = $year + 3760;
1748 # Month number when September = 1, August = 12
1750 if ( $month > 12 ) {
1757 # Calculate day of year from 1 September
1759 for ( $i = 1; $i < $month; $i++ ) {
1763 # Check if the year is leap
1764 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1767 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1774 # Calculate the start of the Hebrew year
1775 $start = self::hebrewYearStart( $hebrewYear );
1777 # Calculate next year's start
1778 if ( $dayOfYear <= $start ) {
1779 # Day is before the start of the year - it is the previous year
1781 $nextStart = $start;
1785 # Add days since previous year's 1 September
1787 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1791 # Start of the new (previous) year
1792 $start = self::hebrewYearStart( $hebrewYear );
1795 $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1798 # Calculate Hebrew day of year
1799 $hebrewDayOfYear = $dayOfYear - $start;
1801 # Difference between year's days
1802 $diff = $nextStart - $start;
1803 # Add 12 (or 13 for leap years) days to ignore the difference between
1804 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1805 # difference is only about the year type
1806 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1812 # Check the year pattern, and is leap year
1813 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1814 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1815 # and non-leap years
1816 $yearPattern = $diff % 30;
1817 # Check if leap year
1818 $isLeap = $diff >= 30;
1820 # Calculate day in the month from number of day in the Hebrew year
1821 # Don't check Adar - if the day is not in Adar, we will stop before;
1822 # if it is in Adar, we will use it to check if it is Adar I or Adar II
1823 $hebrewDay = $hebrewDayOfYear;
1826 while ( $hebrewMonth <= 12 ) {
1827 # Calculate days in this month
1828 if ( $isLeap && $hebrewMonth == 6 ) {
1829 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1831 if ( $hebrewDay <= $days ) {
1835 # Subtract the days of Adar I
1836 $hebrewDay -= $days;
1839 if ( $hebrewDay <= $days ) {
1844 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1845 # Cheshvan in a complete year (otherwise as the rule below)
1847 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1848 # Kislev in an incomplete year (otherwise as the rule below)
1851 # Odd months have 30 days, even have 29
1852 $days = 30 - ( $hebrewMonth - 1 ) % 2;
1854 if ( $hebrewDay <= $days ) {
1855 # In the current month
1858 # Subtract the days of the current month
1859 $hebrewDay -= $days;
1860 # Try in the next month
1865 return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1878 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1879 $b = intval( ( $year - 1 ) % 4 );
1880 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1884 $Mar = intval( $m );
1890 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1891 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1893 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1895 } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1899 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1916 $gy = substr( $ts, 0, 4 );
1917 $gm = substr( $ts, 4, 2 );
1918 $gd = substr( $ts, 6, 2 );
1920 if ( !strcmp( $cName,
'thai' ) ) {
1922 # Add 543 years to the Gregorian calendar
1923 # Months and days are identical
1924 $gy_offset = $gy + 543;
1925 # fix for dates between 1912 and 1941
1927 if ( $gy >= 1912 && $gy <= 1940 ) {
1931 $gm = ( $gm - 3 ) % 12;
1933 } elseif ( ( !strcmp( $cName,
'minguo' ) ) || !strcmp( $cName,
'juche' ) ) {
1935 # Deduct 1911 years from the Gregorian calendar
1936 # Months and days are identical
1937 $gy_offset = $gy - 1911;
1938 } elseif ( !strcmp( $cName,
'tenno' ) ) {
1939 # Nengō dates up to Meiji period
1940 # Deduct years from the Gregorian calendar
1941 # depending on the nengo periods
1942 # Months and days are identical
1944 || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1945 || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1948 $gy_gannen = $gy - 1868 + 1;
1949 $gy_offset = $gy_gannen;
1950 if ( $gy_gannen == 1 ) {
1953 $gy_offset =
'明治' . $gy_offset;
1955 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1956 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1957 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1958 ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1959 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1962 $gy_gannen = $gy - 1912 + 1;
1963 $gy_offset = $gy_gannen;
1964 if ( $gy_gannen == 1 ) {
1967 $gy_offset =
'大正' . $gy_offset;
1969 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1970 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1971 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1974 $gy_gannen = $gy - 1926 + 1;
1975 $gy_offset = $gy_gannen;
1976 if ( $gy_gannen == 1 ) {
1979 $gy_offset =
'昭和' . $gy_offset;
1981 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd >= 8 ) ) ||
1982 ( ( $gy > 1989 ) && ( $gy < 2019 ) ) ||
1983 ( ( $gy == 2019 ) && ( $gm < 5 ) )
1986 $gy_gannen = $gy - 1989 + 1;
1987 $gy_offset = $gy_gannen;
1988 if ( $gy_gannen == 1 ) {
1991 $gy_offset =
'平成' . $gy_offset;
1994 $gy_gannen = $gy - 2019 + 1;
1995 $gy_offset = $gy_gannen;
1996 if ( $gy_gannen == 1 ) {
1999 $gy_offset =
'令和' . $gy_offset;
2005 return [ $gy_offset, $gm, $gd ];
2022 if ( !preg_match( self::$strongDirRegex, $text,
$matches ) ) {
2040 [
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ],
2041 [
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ],
2042 [
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ],
2043 [
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
2044 'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' ]
2047 $num = intval( $num );
2048 if ( $num > 10000 || $num <= 0 ) {
2053 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2054 if ( $num >= $pow10 ) {
2055 $s .= $table[$i][(int)floor( $num / $pow10 )];
2057 $num = $num % $pow10;
2071 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ],
2072 [
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ],
2085 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ]
2088 $num = intval( $num );
2089 if ( $num > 9999 || $num <= 0 ) {
2094 if ( $num === 1000 ) {
2096 } elseif ( $num % 1000 === 0 ) {
2097 return $table[0][$num / 1000] .
"' אלפים";
2102 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2103 if ( $num >= $pow10 ) {
2104 if ( $num === 15 || $num === 16 ) {
2105 $letters[] = $table[0][9];
2106 $letters[] = $table[0][$num - 9];
2109 $letters = array_merge(
2111 (
array)$table[$i][intval( $num / $pow10 )]
2114 if ( $pow10 === 1000 ) {
2120 $num = $num % $pow10;
2123 $preTransformLength = count( $letters );
2124 if ( $preTransformLength === 1 ) {
2128 $lastIndex = $preTransformLength - 1;
2129 $letters[$lastIndex] = str_replace(
2130 [
'כ',
'מ',
'נ',
'פ',
'צ' ],
2131 [
'ך',
'ם',
'ן',
'ף',
'ץ' ],
2132 $letters[$lastIndex]
2138 if ( $letters[1] ===
"'" && $preTransformLength === 3 ) {
2141 array_splice( $letters, -1, 0,
'"' );
2145 return implode( $letters );
2159 if ( $tz ===
false ) {
2160 $tz = $wgUser->getOption(
'timecorrection' );
2163 $data = explode(
'|', $tz, 3 );
2165 if (
$data[0] ==
'ZoneInfo' ) {
2167 $userTZ =
new DateTimeZone(
$data[2] );
2168 $date =
new DateTime( $ts,
new DateTimeZone(
'UTC' ) );
2169 $date->setTimezone( $userTZ );
2170 return $date->format(
'YmdHis' );
2171 }
catch ( Exception
$e ) {
2173 $data[0] =
'Offset';
2177 if (
$data[0] ==
'System' || $tz ==
'' ) {
2178 # Global offset in minutes.
2180 } elseif (
$data[0] ==
'Offset' ) {
2181 $minDiff = intval(
$data[1] );
2183 $data = explode(
':', $tz );
2184 if ( count(
$data ) == 2 ) {
2188 if (
$data[0] < 0 ) {
2189 $minDiff = -$minDiff;
2192 $minDiff = intval(
$data[0] ) * 60;
2196 # No difference ? Return time unchanged
2197 if ( $minDiff == 0 ) {
2201 Wikimedia\suppressWarnings();
2202 # Generate an adjusted date; take advantage of the fact that mktime
2203 # will normalize out-of-range values so we don't have to split $minDiff
2204 # into hours and minutes.
2206 (
int)substr( $ts, 8, 2 ) ), # Hours
2207 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2208 (
int)substr( $ts, 12, 2 ), # Seconds
2209 (
int)substr( $ts, 4, 2 ), # Month
2210 (
int)substr( $ts, 6, 2 ), # Day
2211 (
int)substr( $ts, 0, 4 ) ); # Year
2213 $date =
date(
'YmdHis',
$t );
2214 Wikimedia\restoreWarnings();
2237 if ( is_bool( $usePrefs ) ) {
2239 $datePreference = $wgUser->getDatePreference();
2244 $datePreference = (
string)$usePrefs;
2248 if ( $datePreference ==
'' ) {
2252 return $datePreference;
2266 $wasDefault =
false;
2267 if ( $pref ==
'default' ) {
2272 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2273 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2275 if (
$type ===
'pretty' && $df ===
null ) {
2279 if ( !$wasDefault && $df ===
null ) {
2281 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2284 $this->dateFormatStrings[
$type][$pref] = $df;
2286 return $this->dateFormatStrings[
$type][$pref];
2299 public function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2302 $ts = $this->
userAdjust( $ts, $timecorrection );
2318 public function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2321 $ts = $this->
userAdjust( $ts, $timecorrection );
2338 public function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2341 $ts = $this->
userAdjust( $ts, $timecorrection );
2362 foreach ( $intervals
as $intervalName => $intervalValue ) {
2365 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2366 $segments[] = $message->inLanguage( $this )->escaped();
2384 if ( empty( $chosenIntervals ) ) {
2385 $chosenIntervals = [
2397 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2398 $sortedNames = array_keys( $intervals );
2399 $smallestInterval = array_pop( $sortedNames );
2403 foreach ( $intervals
as $name => $length ) {
2404 $value = floor( $seconds / $length );
2406 if (
$value > 0 || (
$name == $smallestInterval && empty( $segments ) ) ) {
2407 $seconds -=
$value * $length;
2436 $options += [
'timecorrection' =>
true,
'format' =>
true ];
2437 if (
$options[
'timecorrection'] !==
false ) {
2438 if (
$options[
'timecorrection'] ===
true ) {
2439 $offset =
$user->getOption(
'timecorrection' );
2441 $offset =
$options[
'timecorrection'];
2445 if (
$options[
'format'] ===
true ) {
2446 $format =
$user->getDatePreference();
2541 if ( $relativeTo ===
null ) {
2544 if (
$user ===
null ) {
2545 $user = RequestContext::getMain()->getUser();
2550 $offsetRel = $relativeTo->offsetForUser(
$user );
2553 if ( Hooks::run(
'GetHumanTimestamp', [ &$ts,
$time, $relativeTo,
$user, $this ] ) ) {
2558 $time->timestamp->sub( $offsetThis );
2559 $relativeTo->timestamp->sub( $offsetRel );
2578 $diff = $ts->diff( $relativeTo );
2579 $diffDay = (bool)( (
int)$ts->timestamp->format(
'w' ) -
2580 (int)$relativeTo->timestamp->format(
'w' ) );
2581 $days = $diff->days ?: (int)$diffDay;
2582 if ( $diff->invert || $days > 5
2583 && $ts->timestamp->format(
'Y' ) !== $relativeTo->timestamp->format(
'Y' )
2591 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2592 } elseif ( $days > 5 ) {
2595 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2596 } elseif ( $days > 1 ) {
2599 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2603 ->inLanguage( $this )
2604 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2606 } elseif ( $days == 1 ) {
2610 ->inLanguage( $this )
2611 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2613 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2617 ->inLanguage( $this )
2618 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2623 } elseif ( $diff->h == 1 ) {
2625 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2626 } elseif ( $diff->i >= 1 ) {
2628 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2629 } elseif ( $diff->s >= 30 ) {
2631 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2645 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2652 return self::$dataCache->getItem( $this->mCode,
'messages' );
2663 # *input* string. We just ignore those too.
2666 Wikimedia\suppressWarnings();
2667 $text =
iconv( $in,
$out .
'//IGNORE', $string );
2668 Wikimedia\restoreWarnings();
2687 return mb_strtoupper(
$matches[0] );
2695 return mb_strtoupper(
$matches[0] );
2709 } elseif ( $o < 128 ) {
2713 return $this->
uc( $str,
true );
2725 public function uc( $str, $first =
false ) {
2728 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2733 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2744 return strval( $str );
2745 } elseif ( $o >= 128 ) {
2746 return $this->
lc( $str,
true );
2747 } elseif ( $o > 96 ) {
2750 $str[0] = strtolower( $str[0] );
2760 function lc( $str, $first =
false ) {
2763 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2765 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2768 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2777 return strlen( $str ) !== mb_strlen( $str );
2786 $str = $this->
lc( $str );
2789 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2792 return preg_replace_callback(
2794 [ $this,
'ucwordsCallbackMB' ],
2798 return ucwords( strtolower( $str ) );
2810 $str = $this->
lc( $str );
2813 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2816 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2817 "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2819 return preg_replace_callback(
2821 [ $this,
'ucwordbreaksCallbackMB' ],
2825 return preg_replace_callback(
2826 '/\b([\w\x80-\xff]+)\b/',
2827 [ $this,
'ucwordbreaksCallbackAscii' ],
2849 return $this->
uc(
$s );
2858 if ( is_array(
$s ) ) {
2859 throw new MWException(
'Given array to checkTitleEncoding.' );
2872 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2906 return self::convertDoubleWidth( $string );
2918 static $full =
null;
2919 static $half =
null;
2921 if ( $full ===
null ) {
2922 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2923 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2924 $full = str_split( $fullWidth, 3 );
2925 $half = str_split( $halfWidth );
2928 $string = str_replace( $full, $half, $string );
2938 $string = preg_replace( $pattern,
" $1 ", $string );
2939 $string = preg_replace(
'/ +/',
' ', $string );
2948 # some languages, e.g. Chinese, need to do a conversion
2949 # in order for search results to be displayed correctly
2962 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2963 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2969 if ( strlen(
$matches[1] ) != 3 ) {
2977 } elseif (
$code < 0xb098 ) {
2979 } elseif (
$code < 0xb2e4 ) {
2981 } elseif (
$code < 0xb77c ) {
2983 } elseif (
$code < 0xb9c8 ) {
2985 } elseif (
$code < 0xbc14 ) {
2987 } elseif (
$code < 0xc0ac ) {
2989 } elseif (
$code < 0xc544 ) {
2991 } elseif (
$code < 0xc790 ) {
2993 } elseif (
$code < 0xcc28 ) {
2995 } elseif (
$code < 0xce74 ) {
2997 } elseif (
$code < 0xd0c0 ) {
2999 } elseif (
$code < 0xd30c ) {
3001 } elseif (
$code < 0xd558 ) {
3052 $s = UtfNormal\Validator::cleanUp(
$s );
3076 if ( !isset( $this->transformData[
$file] ) ) {
3078 $data = require
"$IP/languages/data/{$file}";
3081 return $this->transformData[
$file]->replace( $string );
3090 return self::$dataCache->getItem( $this->mCode,
'rtl' );
3098 return $this->
isRTL() ?
'rtl' :
'ltr';
3110 return $this->
isRTL() ?
'right' :
'left';
3122 return $this->
isRTL() ?
'left' :
'right';
3138 return $this->
isRTL() ?
'‎' :
'‏';
3140 return $this->
isRTL() ?
'‏' :
'‎';
3154 $lrm =
"\u{200E}"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3155 $rlm =
"\u{200F}"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3157 return $this->
isRTL() ? $lrm : $rlm;
3159 return $this->
isRTL() ? $rlm : $lrm;
3166 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
3177 switch ( $direction ) {
3179 return $this->
isRTL() ?
'←' :
'→';
3181 return $this->
isRTL() ?
'→' :
'←';
3199 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
3207 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
3216 $rawEntry = $this->mMagicExtensions[$mw->mId] ??
3217 self::$dataCache->getSubitem( $this->mCode,
'magicWords', $mw->mId );
3219 if ( !is_array( $rawEntry ) ) {
3220 wfWarn(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3222 $mw->mCaseSensitive = $rawEntry[0];
3223 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3234 $fallbackChain = array_reverse( $fallbackChain );
3235 foreach ( $fallbackChain
as $code ) {
3236 if ( isset( $newWords[
$code] ) ) {
3237 $this->mMagicExtensions = $newWords[
$code] + $this->mMagicExtensions;
3249 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3251 $this->mExtendedSpecialPageAliases =
3252 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3255 return $this->mExtendedSpecialPageAliases;
3265 return "<em>$text</em>";
3292 if ( !$nocommafy ) {
3293 $number = $this->
commafy( $number );
3296 $number = strtr( $number,
$s );
3303 $number = strtr( $number,
$s );
3307 return (
string)$number;
3319 return $this->
formatNum( $number,
true );
3330 $s = array_filter(
$s );
3331 $number = strtr( $number, array_flip(
$s ) );
3337 $s = array_filter(
$s );
3338 $number = strtr( $number, array_flip(
$s ) );
3341 $number = strtr( $number, [
',' =>
'' ] );
3354 if ( $number ===
null ) {
3365 $primaryGroupingSize = 3;
3368 if ( preg_match(
'/^\-?\d{1,' . $maximumLength .
'}(\.\d+)?$/', $number ) ) {
3372 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3376 if ( intval( $number ) < 0 ) {
3379 $number = substr( $number, 1 );
3384 preg_match(
"/\d+/", $number, $integerPart );
3385 preg_match(
"/\.\d*/", $number, $decimalPart );
3386 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3387 if ( $groupedNumber === $number ) {
3389 return $sign . $groupedNumber;
3391 $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3392 while ( $start > 0 ) {
3393 $match =
$matches[0][$numMatches - 1];
3394 $matchLen = strlen( $match );
3395 $start = $end - $matchLen;
3399 $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3401 if ( $numMatches > 1 ) {
3406 $groupedNumber =
"," . $groupedNumber;
3409 return $sign . $groupedNumber;
3417 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3424 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3431 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3438 return self::$dataCache->getItem( $this->mCode,
'minimumGroupingDigits' );
3450 $itemCount = count( $list );
3451 if ( $itemCount < 1 ) {
3454 $text = array_pop( $list );
3455 if ( $itemCount > 1 ) {
3456 $and = $this->
msg(
'and' )->escaped();
3457 $space = $this->
msg(
'word-separator' )->escaped();
3459 if ( $itemCount > 2 ) {
3460 $comma = $this->
msg(
'comma-separator' )->escaped();
3462 $text = implode( $comma, $list ) . $and . $space . $text;
3475 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3488 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3500 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3522 $string, $length, $ellipsis, $adjustLength,
'strlen',
'substr'
3550 $string, $length, $ellipsis, $adjustLength,
'mb_strlen',
'mb_substr'
3571 $string, $length, $ellipsis, $adjustLength, callable $measureLength, callable $getSubstring
3573 # Check if there is no need to truncate
3574 if ( $measureLength( $string ) <= abs( $length ) ) {
3578 # Use the localized ellipsis character
3579 if ( $ellipsis ==
'...' ) {
3580 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3582 if ( $length == 0 ) {
3586 $stringOriginal = $string;
3587 # If ellipsis length is >= $length then we can't apply $adjustLength
3588 if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3589 $string = $ellipsis;
3590 # Otherwise, truncate and add ellipsis...
3592 $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3593 if ( $length > 0 ) {
3594 $length -= $ellipsisLength;
3595 $string = $getSubstring( $string, 0, $length );
3597 $string = rtrim( $string ) . $ellipsis;
3599 $length += $ellipsisLength;
3600 $string = $getSubstring( $string, $length );
3602 $string = $ellipsis . ltrim( $string );
3606 # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3607 # This check is *not* redundant if $adjustLength, due to the single case where
3608 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3609 if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3612 return $stringOriginal;
3624 if ( $string !=
'' ) {
3625 $char = ord( $string[strlen( $string ) - 1] );
3627 if ( $char >= 0xc0 ) {
3628 # We got the first byte only of a multibyte char; remove it.
3629 $string = substr( $string, 0, -1 );
3630 } elseif ( $char >= 0x80 &&
3632 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3633 '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3635 # We chopped in the middle of a character; remove it
3650 if ( $string !=
'' ) {
3651 $char = ord( $string[0] );
3652 if ( $char >= 0x80 && $char < 0xc0 ) {
3653 # We chopped in the middle of a character; remove the whole thing
3654 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3676 # Use the localized ellipsis character
3677 if ( $ellipsis ==
'...' ) {
3678 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3680 # Check if there is clearly no need to truncate
3681 if ( $length <= 0 ) {
3683 } elseif ( strlen( $text ) <= $length ) {
3688 $testingEllipsis =
false;
3696 $textLen = strlen( $text );
3697 $neLength = max( 0, $length - strlen( $ellipsis ) );
3698 for ( $pos = 0;
true; ++$pos ) {
3699 # Consider truncation once the display length has reached the maximim.
3700 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3701 # Check that we're not in the middle of a bracket/entity...
3702 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3703 if ( !$testingEllipsis ) {
3704 $testingEllipsis =
true;
3705 # Save where we are; we will truncate here unless there turn out to
3706 # be so few remaining characters that truncation is not necessary.
3707 if ( !$maybeState ) {
3708 $maybeState = [
$ret, $openTags ];
3710 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3711 # String in fact does need truncation, the truncation point was OK.
3713 list(
$ret, $openTags ) = $maybeState;
3719 if ( $pos >= $textLen ) {
3723 # Read the next char...
3725 $lastCh = $pos ? $text[$pos - 1] :
'';
3731 } elseif ( $ch ==
'>' ) {
3735 } elseif ( $bracketState == 1 ) {
3743 } elseif ( $bracketState == 2 ) {
3750 } elseif ( $bracketState == 0 ) {
3751 if ( $entityState ) {
3757 if ( $neLength == 0 && !$maybeState ) {
3760 $maybeState = [ substr(
$ret, 0, -1 ), $openTags ];
3767 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3769 $dispLen += $skipped;
3777 while ( count( $openTags ) > 0 ) {
3778 $ret .=
'</' . array_pop( $openTags ) .
'>';
3795 if ( $len ===
null ) {
3797 } elseif ( $len < 0 ) {
3801 if ( $start < strlen( $text ) ) {
3802 $skipCount = strcspn( $text, $search, $start, $len );
3803 $ret .= substr( $text, $start, $skipCount );
3818 $tag = ltrim( $tag );
3820 if ( $tagType == 0 && $lastCh !=
'/' ) {
3822 } elseif ( $tagType == 1 ) {
3823 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3824 array_pop( $openTags );
3853 if ( is_string( $forms ) ) {
3857 foreach ( array_values( $forms )
as $rule ) {
3860 if ( $form ===
'@metadata' ) {
3864 $replacement = $rule[1];
3866 $regex =
'/' . addcslashes( $form,
'/' ) .
'/u';
3867 $patternMatches = preg_match( $regex, $word );
3869 if ( $patternMatches ===
false ) {
3871 'An error occurred while processing grammar. ' .
3872 "Word: '$word'. Regex: /$form/."
3874 } elseif ( $patternMatches === 1 ) {
3875 $word = preg_replace( $regex, $replacement, $word );
3911 $languageCode = $this->
getCode();
3913 if ( self::$grammarTransformations ===
null ) {
3914 self::$grammarTransformations =
new MapCacheLRU( 10 );
3917 if ( self::$grammarTransformations->has( $languageCode ) ) {
3918 return self::$grammarTransformations->get( $languageCode );
3923 $grammarDataFile = __DIR__ .
"/data/grammarTransformations/$languageCode.json";
3924 if ( is_readable( $grammarDataFile ) ) {
3925 $data = FormatJson::decode(
3926 file_get_contents( $grammarDataFile ),
3930 if (
$data ===
null ) {
3931 throw new MWException(
"Invalid grammar data for \"$languageCode\"." );
3934 self::$grammarTransformations->set( $languageCode,
$data );
3960 if ( !count( $forms ) ) {
3964 if ( $gender ===
'male' ) {
3967 if ( $gender ===
'female' ) {
3970 return $forms[2] ?? $forms[0];
3991 if ( is_string( $forms ) ) {
3994 if ( !count( $forms ) ) {
3999 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4000 return $forms[$pluralForm];
4019 foreach ( $forms
as $index => $form ) {
4020 if ( preg_match(
'/\d+=/i', $form ) ) {
4021 $pos = strpos( $form,
'=' );
4022 if ( substr( $form, 0, $pos ) === (
string)$count ) {
4023 return substr( $form, $pos + 1 );
4025 unset( $forms[$index] );
4028 return array_values( $forms );
4040 return array_pad( $forms, $count, end( $forms ) );
4060 $dir = self::strongDirFromContent( $text );
4061 if ( $dir ===
'ltr' ) {
4063 return self::$lre . $text . self::$pdf;
4065 if ( $dir ===
'rtl' ) {
4067 return self::$rle . $text . self::$pdf;
4088 foreach ( $duration
as $show =>
$value ) {
4089 if ( strcmp( $str,
$value ) == 0 ) {
4090 return htmlspecialchars( trim( $show ) );
4095 foreach ( $duration
as $show =>
$value ) {
4097 return htmlspecialchars( trim( $show ) );
4103 $time = strtotime( $str, $now );
4104 if (
$time ===
false ) {
4106 } elseif (
$time !== strtotime( $str, $now + 1 ) ) {
4111 if (
$time === 0 ) {
4113 $time =
'19700101000000';
4150 return $this->mConverter;
4162 return $this->mConverter->autoConvert( $text, $variant );
4172 return $this->mConverter->autoConvertToAllVariants( $text );
4187 return $this->mConverter->convert( $text );
4197 return $this->mConverter->convertTitle(
$title );
4209 return $this->mConverter->convertNamespace( $ns, $variant );
4232 return $variant && ( $variant === $this->mConverter->validateVariant( $variant ) );
4242 return htmlspecialchars( $this->
convert( $text ) );
4250 return $this->mConverter->convertCategoryKey( $key );
4260 return $this->mConverter->getVariants();
4267 return $this->mConverter->getPreferredVariant();
4274 return $this->mConverter->getDefaultVariant();
4281 return $this->mConverter->getURLVariant();
4297 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
4307 return $this->mConverter->getExtraHashOptions();
4318 return $this->mConverter->getParsedTitle();
4328 $this->mConverter->updateConversionTable(
$title );
4338 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
4348 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
4359 if ( $this->mParentLanguage !==
false ) {
4360 return $this->mParentLanguage;
4364 if ( !in_array(
$code, LanguageConverter::$languagesWithVariants ) ) {
4365 $this->mParentLanguage =
null;
4369 if ( !
$lang->hasVariant( $this->getCode() ) ) {
4370 $this->mParentLanguage =
null;
4374 $this->mParentLanguage =
$lang;
4386 return $lang === $this ||
$lang->getCode() === $this->mCode;
4398 return $this->mCode;
4412 if ( is_null( $this->mHtmlCode ) ) {
4413 $this->mHtmlCode = LanguageCode::bcp47( $this->
getCode() );
4415 return $this->mHtmlCode;
4424 $this->mCode =
$code;
4426 $this->mHtmlCode =
null;
4427 $this->mParentLanguage =
false;
4439 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4440 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4441 if ( !count( $m ) ) {
4444 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4456 return 'Language' . str_replace(
'-',
'_',
ucfirst(
$code ) );
4469 if ( !self::isValidBuiltInCode(
$code ) ) {
4470 throw new MWException(
"Invalid language code \"$code\"" );
4473 return $prefix . str_replace(
'-',
'_',
ucfirst(
$code ) ) . $suffix;
4482 $file = self::getFileName(
"$IP/languages/messages/Messages",
$code,
'.php' );
4483 Hooks::run(
'Language::getMessagesFileName', [
$code, &
$file ] );
4496 if ( !self::isValidBuiltInCode(
$code ) ) {
4497 throw new MWException(
"Invalid language code \"$code\"" );
4500 return "$IP/languages/i18n/$code.json";
4511 $fallbacks = self::getFallbacksFor(
$code );
4513 return $fallbacks[0];
4529 if (
$code ===
'en' || !self::isValidBuiltInCode(
$code ) ) {
4533 case self::MESSAGES_FALLBACKS:
4537 return self::getLocalisationCache()->getItem(
$code,
'fallbackSequence' ) ?: [
'en' ];
4538 case self::STRICT_FALLBACKS:
4542 return self::getLocalisationCache()->getItem(
$code,
'originalFallbackSequence' );
4544 throw new MWException(
"Invalid fallback mode \"$mode\"" );
4561 $cacheKey =
"{$code}-{$wgLanguageCode}";
4563 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4564 $fallbacks = self::getFallbacksFor(
$code );
4571 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4573 self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4575 return self::$fallbackLanguageCache[$cacheKey];
4588 return self::getLocalisationCache()->getItem(
$code,
'messages' );
4600 return self::getLocalisationCache()->getSubitem(
$code,
'messages', $key );
4612 return self::getLocalisationCache()->getSubitemList(
$code,
'messages' );
4620 if ( strpos( $talk,
'$1' ) ===
false ) {
4627 # Allow grammar transformations
4628 # Allowing full message-style parsing would make simple requests
4629 # such as action=raw much more expensive than they need to be.
4630 # This will hopefully cover most cases.
4631 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4632 [ $this,
'replaceGrammarInNamespace' ], $talk );
4633 return str_replace(
' ',
'_', $talk );
4654 public function formatExpiry( $expiry, $format =
true, $infinity =
'infinity' ) {
4656 if ( $dbInfinity ===
null ) {
4660 if ( $expiry ==
'' || $expiry ===
'infinity' || $expiry == $dbInfinity ) {
4661 return $format ===
true
4665 return $format ===
true
4685 if ( !is_array( $format ) ) {
4686 $format = [
'avoid' => $format ];
4688 if ( !isset( $format[
'avoid'] ) ) {
4689 $format[
'avoid'] =
false;
4691 if ( !isset( $format[
'noabbrevs'] ) ) {
4692 $format[
'noabbrevs'] =
false;
4695 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4697 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4699 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4701 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4703 if ( round( $seconds * 10 ) < 100 ) {
4704 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4705 $s = $secondsMsg->params(
$s )->text();
4706 } elseif ( round( $seconds ) < 60 ) {
4708 $s = $secondsMsg->params(
$s )->text();
4709 } elseif ( round( $seconds ) < 3600 ) {
4710 $minutes = floor( $seconds / 60 );
4711 $secondsPart = round( fmod( $seconds, 60 ) );
4712 if ( $secondsPart == 60 ) {
4716 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4718 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4719 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4720 $hours = floor( $seconds / 3600 );
4721 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4722 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4723 if ( $secondsPart == 60 ) {
4727 if ( $minutes == 60 ) {
4731 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4733 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4734 if ( !in_array( $format[
'avoid'], [
'avoidseconds',
'avoidminutes' ] ) ) {
4735 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4738 $days = floor( $seconds / 86400 );
4739 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4740 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4741 if ( $hours == 24 ) {
4745 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4747 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4748 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4749 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4750 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4751 if ( $minutes == 60 ) {
4755 if ( $hours == 24 ) {
4759 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4761 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4763 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4765 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4796 return str_replace(
'$1', $this->
formatNum( $size ),
4800 $sizes = [
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' ];
4803 $maxIndex = count( $sizes ) - 1;
4804 while ( $size >= $boundary && $index < $maxIndex ) {
4815 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4817 $size = round( $size, $round );
4819 return str_replace(
'$1', $this->
formatNum( $size ), $text );
4850 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) . $this->getDirMark();
4853 $this->
msg(
'word-separator' )->escaped() .
4854 $this->
msg(
'parentheses' )->rawParams( $details )->escaped();
4874 # Make 'previous' link
4875 $prev =
wfMessage(
'prevn' )->inLanguage( $this )->title(
$title )->numParams( $limit )->text();
4876 if ( $offset > 0 ) {
4877 $plink = $this->
numLink(
$title, max( $offset - $limit, 0 ), $limit,
4878 $query, $prev,
'prevn-title',
'mw-prevlink' );
4880 $plink = htmlspecialchars( $prev );
4884 $next =
wfMessage(
'nextn' )->inLanguage( $this )->title(
$title )->numParams( $limit )->text();
4886 $nlink = htmlspecialchars( $next );
4889 $query, $next,
'nextn-title',
'mw-nextlink' );
4892 # Make links to set number of items per page
4894 foreach ( [ 20, 50, 100, 250, 500 ]
as $num ) {
4899 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title(
$title
4900 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
4918 $query = [
'limit' => $limit,
'offset' => $offset ] +
$query;
4919 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title(
$title )
4920 ->numParams( $limit )->text();
4922 return Html::element(
'a', [
'href' =>
$title->getLocalURL(
$query ),
4923 'title' => $tooltip,
'class' => $class ],
$link );
4932 return $this->mConverter->getConvRuleTitle();
4941 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
4942 $fallbacks = self::getFallbacksFor( $this->mCode );
4943 if ( !$pluralRules ) {
4944 foreach ( $fallbacks
as $fallbackCode ) {
4945 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
4946 if ( $pluralRules ) {
4951 return $pluralRules;
4960 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
4961 $fallbacks = self::getFallbacksFor( $this->mCode );
4962 if ( !$pluralRules ) {
4963 foreach ( $fallbacks
as $fallbackCode ) {
4964 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
4965 if ( $pluralRules ) {
4970 return $pluralRules;
4979 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
4980 $fallbacks = self::getFallbacksFor( $this->mCode );
4981 if ( !$pluralRuleTypes ) {
4982 foreach ( $fallbacks
as $fallbackCode ) {
4983 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
4984 if ( $pluralRuleTypes ) {
4989 return $pluralRuleTypes;
4999 $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5014 return $pluralRuleTypes[$index] ??
'other';
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgLanguageCode
Site language code.
$wgExtraGenderNamespaces
Same as above, but for namespaces with gender distinction.
$wgTranslateNumerals
For Hindi and Arabic use local numerals instead of Western style (0-9) numerals in interface.
$wgGrammarForms
Some languages need different word forms, usually for different cases.
$wgExtraNamespaces
Additional namespaces.
$wgAllUnicodeFixes
Set this to always convert certain Unicode sequences to modern ones regardless of the content languag...
$wgDummyLanguageCodes
Functionally the same as $wgExtraLanguageCodes, but deprecated.
$wgNamespaceAliases
Namespace aliases.
$wgMetaNamespace
Name of the project namespace.
$wgMetaNamespaceTalk
Name of the project talk namespace.
$wgLocalTZoffset
Set an offset from UTC in minutes to use for the default timezone setting for anonymous users and new...
$wgLocalisationCacheConf
Localisation cache configuration.
$wgLangObjCacheSize
Language cache size, or really how many languages can we handle simultaneously without degrading to c...
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
A fake language variant converter.
Simple store for keeping values in an associative array for the current process.
Base class for language conversion.
Internationalisation code.
const STRICT_FALLBACKS
Return a strict fallback chain in getFallbacksFor.
const MESSAGES_FALLBACKS
Return a fallback chain for messages in getFallbacksFor.
hasVariants()
Check if this is a language with variants.
initContLang()
Hook which will be called if this is the content language.
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
date( $ts, $adj=false, $format=true, $timecorrection=false)
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
static $strongDirRegex
Directionality test regex for embedBidi().
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
getPluralRuleTypes()
Get the plural rule types for the language.
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
convertCategoryKey( $key)
array null $namespaceNames
static getMessageKeysFor( $code)
Get all message keys for a given language.
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
static $mHebrewCalendarMonthGenMsgs
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
getHebrewCalendarMonthName( $key)
static getMessageFor( $key, $code)
Get a message for a given language.
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
truncateForVisual( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified number of characters, appending an optional string (e....
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
static clearCaches()
Intended for tests that may change configuration in a way that invalidates caches.
static array $fallbackLanguageCache
Cache for language fallbacks.
getMonthAbbreviation( $key)
getHebrewCalendarMonthNameGen( $key)
static $lre
Unicode directional formatting characters, for embedBidi()
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
hasVariant( $variant)
Strict check if the language has the specific variant.
static classFromCode( $code, $fallback=true)
transformUsingPairFile( $file, $string)
Transform a string using serialized data stored in the given file (which must be in the serialized su...
formatDuration( $seconds, array $chosenIntervals=[])
Takes a number of seconds and turns it into a text using values such as hours and minutes.
getHijriCalendarMonthName( $key)
const ALL
Return all known languages in fetchLanguageName(s).
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
ucwordbreaksCallbackAscii( $matches)
static LocalisationCache $dataCache
getPluralRuleType( $number)
Find the plural rule type appropriate for the given number For example, if the language is set to Ara...
normalize( $s)
Convert a UTF-8 string to normal form C.
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
getArrow( $direction='forwards')
An arrow, depending on the language direction.
equals(Language $lang)
Compare with an other language object.
ucwordbreaksCallbackMB( $matches)
convert( $text)
convert text to different variants of a language.
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
translateBlockExpiry( $str, User $user=null, $now=0)
truncateHtml( $text, $length, $ellipsis='...')
Truncate a string of valid HTML to a specified length in bytes, appending an optional string (e....
getGrammarTransformations()
Get the grammar transformations data for the language.
__destruct()
Reduce memory usage.
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
truncate_skip(&$ret, $text, $search, $start, $len=null)
truncateHtml() helper function like strcspn() but adds the skipped chars to $ret
getFormattedNamespaces()
A convenience function that returns getNamespaces() with spaces instead of underscores in values.
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
$transformData
ReplacementArray object caches.
userTimeAndDate( $ts, User $user, array $options=[])
Get the formatted date and time for the given timestamp and formatted for the given user.
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
commafy( $number)
Adds commas to a given number.
truncate_endBracket(&$tag, $tagType, $lastCh, &$openTags)
truncateHtml() helper function (a) push or pop $tag from $openTags as needed (b) clear $tag value
firstChar( $s)
Get the first character of a string.
$mExtendedSpecialPageAliases
getGrammarForms()
Get the grammar forms for the content language.
static getFallbacksFor( $code, $mode=self::MESSAGES_FALLBACKS)
Get the ordered list of fallback languages.
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
static convertDoubleWidth( $string)
convert double-width roman characters to single-width.
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
getHtmlCode()
Get the code in BCP 47 format which we can use inside of html lang="" tags.
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
static array $durationIntervals
getNsIndex( $text)
Get a namespace key by value, case insensitive.
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
listToText(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
LanguageConverter $mConverter
caseFold( $s)
Return a case-folded representation of $s.
truncateInternal( $string, $length, $ellipsis, $adjustLength, callable $measureLength, callable $getSubstring)
Internal method used for truncation.
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
getCode()
Get the internal language code for this language object.
static $mIranianCalendarMonthMsgs
uc( $str, $first=false)
Convert a string to uppercase.
static $mHebrewCalendarMonthMsgs
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
truncateForDatabase( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
static newFromCode( $code, $fallback=false)
Create a language object for a given language code.
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
getMonthAbbreviationsArray()
static fetchLanguageName( $code, $inLanguage=self::AS_AUTONYMS, $include=self::ALL)
getMagic( $mw)
Fill a MagicWord object with data from here.
viewPrevNext(Title $title, $offset, $limit, array $query=[], $atend=false)
Generate (prev x| next x) (20|50|100...) type links for paging.
resetNamespaces()
Resets all of the namespace caches.
getMagicWords()
Get all magic words from cache.
emphasize( $text)
Italic is unsuitable for some languages.
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
convertForSearchResult( $termsArray)
sprintfDate( $format, $ts, DateTimeZone $zone=null, &$ttl='unused')
This is a workalike of PHP's date() function, but with better internationalisation,...
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
static HashBagOStuff null $languageNameCache
Cache for language names.
autoConvert( $text, $variant=false)
convert text to a variant
getIranianCalendarMonthName( $key)
getHumanTimestampInternal(MWTimestamp $ts, MWTimestamp $relativeTo, User $user)
Convert an MWTimestamp into a pretty human-readable timestamp using the given user preferences and re...
getHumanTimestamp(MWTimestamp $time, MWTimestamp $relativeTo=null, User $user=null)
Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
convertNamespace( $ns, $variant=null)
Convert a namespace index to a string in the preferred variant.
preConvertPlural($forms, $count)
Checks that convertPlural was given an array and pads it to requested amount of forms by copying the ...
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
static getFallbackFor( $code)
Get the first fallback for a given language.
getWeekdayAbbreviation( $key)
isRTL()
For right-to-left language support.
separatorTransformTable()
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
replaceGrammarInNamespace( $m)
getBookstoreList()
Exports $wgBookstoreListEn.
getDir()
Return the correct HTML 'dir' attribute value for this language.
parseFormattedNumber( $number)
static factory( $code)
Get a cached or new language object for a given language code.
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
userDate( $ts, User $user, array $options=[])
Get the formatted date for the given timestamp and formatted for the given user.
getPluralRules()
Get the plural rules for the language.
autoConvertToAllVariants( $text)
convert text to all supported variants
const AS_AUTONYMS
Return autonyms in fetchLanguageName(s).
iconv( $in, $out, $string)
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
fixVariableInNamespace( $talk)
static insertSpace( $string, $pattern)
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
const SUPPORTED
Return in fetchLanguageName(s) only the languages for which we have at least some localisation.
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
static getLocalisationCache()
Get the LocalisationCache instance.
static getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
msg( $msg)
Get message object in this language.
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
static getJsonMessagesFileName( $code)
ucwordbreaks( $str)
capitalize words at word breaks
ucfirst( $str)
Make a string's first character uppercase.
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
userTime( $ts, User $user, array $options=[])
Get the formatted time for the given timestamp and formatted for the given user.
getNsText( $index)
Get a namespace value by key.
hasWordBreaks()
Most writing systems use whitespace to break up words.
getConvRuleTitle()
Get the conversion rule title, if any.
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
static getMessagesFileName( $code)
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
time( $ts, $adj=false, $format=true, $timecorrection=false)
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
static $mHijriCalendarMonthMsgs
static $mWeekdayAbbrevMsgs
unsegmentForDiff( $text)
and unsegment to show the result
formatComputingNumbers( $size, $boundary, $messageKey)
ucwordsCallbackMB( $matches)
getPluralRuleIndexNumber( $number)
Find the index number of the plural rule appropriate for the given number.
updateConversionTable(Title $title)
Refresh the cache of conversion tables when MediaWiki:Conversiontable* is updated.
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
checkTitleEncoding( $s)
TODO: $s is not always a string per T218883.
convertHtml( $text)
Perform output conversion on a string, and encode for safe HTML output.
getConverter()
Return the LanguageConverter used in the Language.
static tsToHijri( $ts)
Converting Gregorian dates to Hijri dates.
static isValidCode( $code)
Returns true if a language code string is of a valid form, whether or not it exists.
static romanNumeral( $num)
Roman number formatting up to 10000.
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
getCompiledPluralRules()
Get the compiled plural rules for the language.
Class for caching the contents of localisation files, Messages*.php and *.i18n.php.
Library for creating and parsing MW-style timestamps.
Handles a simple LRU key/value map with a maximum number of entries.
Wrapper around strtr() that holds replacements.
static getSuggestedDurations(Language $lang=null, $includeOther=true)
Get an array of suggested block durations from MediaWiki:Ipboptions.
static isUtf8( $value)
Test whether a string is valid UTF-8.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static getDefaultOption( $opt)
Get a given default option value.
=Architecture==Two class hierarchies are used to provide the functionality associated with the different content models:*Content interface(and AbstractContent base class) define functionality that acts on the concrete content of a page, and *ContentHandler base class provides functionality specific to a content model, but not acting on concrete content. The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content. These Content objects are to be used by MediaWiki everywhere, instead of passing page content around as text. All manipulation and analysis of page content must be done via the appropriate methods of the Content object. For each content model, a subclass of ContentHandler has to be registered with $wgContentHandlers. The ContentHandler object for a given content model can be obtained using ContentHandler::getForModelID($id). Also Title, WikiPage and Revision now have getContentHandler() methods for convenience. ContentHandler objects are singletons that provide functionality specific to the content type, but not directly acting on the content of some page. ContentHandler::makeEmptyContent() and ContentHandler::unserializeContent() can be used to create a Content object of the appropriate type. However, it is recommended to instead use WikiPage::getContent() resp. Revision::getContent() to get a page 's content as a Content object. These two methods should be the ONLY way in which page content is accessed. Another important function of ContentHandler objects is to define custom action handlers for a content model, see ContentHandler::getActionOverrides(). This is similar to what WikiPage::getActionOverrides() was already doing.==Serialization==With the ContentHandler facility, page content no longer has to be text based. Objects implementing the Content interface are used to represent and handle the content internally. For storage and data exchange, each content model supports at least one serialization format via ContentHandler::serializeContent($content). The list of supported formats for a given content model can be accessed using ContentHandler::getSupportedFormats(). Content serialization formats are identified using MIME type like strings. The following formats are built in:*text/x-wiki - wikitext *text/javascript - for js pages *text/css - for css pages *text/plain - for future use, e.g. with plain text messages. *text/html - for future use, e.g. with plain html messages. *application/vnd.php.serialized - for future use with the api and for extensions *application/json - for future use with the api, and for use by extensions *application/xml - for future use with the api, and for use by extensions In PHP, use the corresponding CONTENT_FORMAT_XXX constant. Note that when using the API to access page content, especially action=edit, action=parse and action=query &prop=revisions, the model and format of the content should always be handled explicitly. Without that information, interpretation of the provided content is not reliable. The same applies to XML dumps generated via maintenance/dumpBackup.php or Special:Export. Also note that the API will provide encapsulated, serialized content - so if the API was called with format=json, and contentformat is also json(or rather, application/json), the page content is represented as a string containing an escaped json structure. Extensions that use JSON to serialize some types of page content may provide specialized API modules that allow access to that content in a more natural form.==Compatibility==The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least for pages that contain wikitext or other text based content. However, a number of functions and hooks have been deprecated in favor of new versions that are aware of the page 's content model, and will now generate warnings when used. Most importantly, the following functions have been deprecated:*Revisions::getText() is deprecated in favor Revisions::getContent() *WikiPage::getText() is deprecated in favor WikiPage::getContent() Also, the old Article::getContent()(which returns text) is superceded by Article::getContentObject(). However, both methods should be avoided since they do not provide clean access to the page 's actual content. For instance, they may return a system message for non-existing pages. Use WikiPage::getContent() instead. Code that relies on a textual representation of the page content should eventually be rewritten. However, ContentHandler::getContentText() provides a stop-gap that can be used to get text for a page. Its behavior is controlled by $wgContentHandlerTextFallback it
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
database rows
see documentation in includes Linker php for Linker::makeImageLink & $time
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
namespace and then decline to actually register it & $namespaces
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
namespace and then decline to actually register it file or subcat img or subcat $title
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
if the prop value should be in the metadata multi language array format
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Allows to change the fields on the form that will be generated $name
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
return true to allow those checks to and false if checking is done & $user
returning false will NOT prevent logging $e
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring which defines all default service and specifies how they depend on each other("wiring"). When a new service is added to MediaWiki core
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
if(!isset( $args[0])) $lang