29use CLDRPluralRuleParser\Evaluator;
65 'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
70 'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
74 'january',
'february',
'march',
'april',
'may_long',
'june',
75 'july',
'august',
'september',
'october',
'november',
79 'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
80 'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
84 'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
85 'sep',
'oct',
'nov',
'dec'
89 'iranian-calendar-m1',
'iranian-calendar-m2',
'iranian-calendar-m3',
90 'iranian-calendar-m4',
'iranian-calendar-m5',
'iranian-calendar-m6',
91 'iranian-calendar-m7',
'iranian-calendar-m8',
'iranian-calendar-m9',
92 'iranian-calendar-m10',
'iranian-calendar-m11',
'iranian-calendar-m12'
96 'hebrew-calendar-m1',
'hebrew-calendar-m2',
'hebrew-calendar-m3',
97 'hebrew-calendar-m4',
'hebrew-calendar-m5',
'hebrew-calendar-m6',
98 'hebrew-calendar-m7',
'hebrew-calendar-m8',
'hebrew-calendar-m9',
99 'hebrew-calendar-m10',
'hebrew-calendar-m11',
'hebrew-calendar-m12',
100 'hebrew-calendar-m6a',
'hebrew-calendar-m6b'
104 'hebrew-calendar-m1-gen',
'hebrew-calendar-m2-gen',
'hebrew-calendar-m3-gen',
105 'hebrew-calendar-m4-gen',
'hebrew-calendar-m5-gen',
'hebrew-calendar-m6-gen',
106 'hebrew-calendar-m7-gen',
'hebrew-calendar-m8-gen',
'hebrew-calendar-m9-gen',
107 'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
108 'hebrew-calendar-m6a-gen',
'hebrew-calendar-m6b-gen'
112 'hijri-calendar-m1',
'hijri-calendar-m2',
'hijri-calendar-m3',
113 'hijri-calendar-m4',
'hijri-calendar-m5',
'hijri-calendar-m6',
114 'hijri-calendar-m7',
'hijri-calendar-m8',
'hijri-calendar-m9',
115 'hijri-calendar-m10',
'hijri-calendar-m11',
'hijri-calendar-m12'
122 static public $durationIntervals = [
123 'millennia' => 31556952000,
124 'centuries' => 3155695200,
125 'decades' => 315569520,
140 static private $fallbackLanguageCache = [];
157 static private $lre =
"\xE2\x80\xAA";
158 static private $rle =
"\xE2\x80\xAB";
159 static private $pdf =
"\xE2\x80\xAC";
174 static private $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';
191 $langObj = isset( self::$mLangObjCache[
$code] )
192 ? self::$mLangObjCache[
$code]
193 : self::newFromCode(
$code );
196 self::$mLangObjCache = array_merge( [
$code => $langObj ], self::$mLangObjCache );
211 throw new MWException(
"Invalid language code \"$code\"" );
223 $class = self::classFromCode(
$code );
224 if ( class_exists( $class ) ) {
231 foreach ( $fallbacks
as $fallbackCode ) {
233 throw new MWException(
"Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
236 $class = self::classFromCode( $fallbackCode );
237 if ( class_exists( $class ) ) {
244 throw new MWException(
"Invalid fallback sequence for language '$code'" );
256 if ( !self::isValidBuiltInCode(
$code ) ) {
260 if (
$code ===
'qqq' ) {
264 return is_readable( self::getMessagesFileName(
$code ) ) ||
265 is_readable( self::getJsonMessagesFileName(
$code ) );
286 $alphanum =
'[a-z0-9]';
287 $x =
'x'; #
private use singleton
288 $singleton =
'[a-wy-z]'; #
other singleton
289 $s = $lenient ?
'[-_]' :
'-';
291 $language =
"$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
292 $script =
"$alpha{4}"; # ISO 15924
293 $region =
"(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2
or UN M.49
294 $variant =
"(?:$alphanum{5,8}|$digit$alphanum{3})";
295 $extension =
"$singleton(?:$s$alphanum{2,8})+";
296 $privateUse =
"$x(?:$s$alphanum{1,8})+";
298 # Define certain grandfathered codes, since otherwise the regex is pretty useless.
299 # Since these are limited, this is safe even later changes to the registry --
300 # the only oddity is that it might change the type of the tag, and thus
301 # the results from the capturing groups.
304 $grandfathered =
"en{$s}GB{$s}oed"
305 .
"|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
306 .
"|no{$s}(?:bok|nyn)"
307 .
"|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
308 .
"|zh{$s}min{$s}nan";
310 $variantList =
"$variant(?:$s$variant)*";
311 $extensionList =
"$extension(?:$s$extension)*";
313 $langtag =
"(?:($language)"
316 .
"(?:$s$variantList)?"
317 .
"(?:$s$extensionList)?"
318 .
"(?:$s$privateUse)?)";
320 # The final breakdown, with capturing groups for each of these components
321 # The variants, extensions, grandfathered, and private-use may have interior '-'
323 $root =
"^(?:$langtag|$privateUse|$grandfathered)$";
325 return (
bool)preg_match(
"/$root/", strtolower(
$code ) );
346 strcspn(
$code,
":/\\\000&<>'\"" ) === strlen(
$code )
364 if ( !is_string(
$code ) ) {
365 if ( is_object(
$code ) ) {
366 $addmsg =
" of class " . get_class(
$code );
371 throw new MWException( __METHOD__ .
" must be passed a string, $type given$addmsg" );
374 return (
bool)preg_match(
'/^[a-z0-9-]{2,}$/',
$code );
388 if ( !self::isValidBuiltInCode(
$tag ) ) {
393 || self::fetchLanguageName(
$tag,
$tag ) !==
''
407 if ( is_null( self::$dataCache ) ) {
412 return self::$dataCache;
418 if ( static::class ===
'Language' ) {
421 $this->mCode = str_replace(
'_',
'-', strtolower( substr( static::class, 8 ) ) );
423 self::getLocalisationCache();
431 unset( $this->
$name );
447 return self::getFallbacksFor( $this->mCode );
455 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
465 if ( is_null( $this->namespaceNames ) ) {
468 $validNamespaces = MWNamespace::getCanonicalNamespaces();
471 self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
472 $this->namespaceNames += $validNamespaces;
483 # Sometimes a language will be localised but not actually exist on this wiki.
484 foreach ( $this->namespaceNames
as $key => $text ) {
485 if ( !isset( $validNamespaces[$key] ) ) {
486 unset( $this->namespaceNames[$key] );
490 # The above mixing may leave namespaces out of canonical order.
491 # Re-order by namespace ID number...
492 ksort( $this->namespaceNames );
494 Hooks::run(
'LanguageGetNamespaces', [ &$this->namespaceNames ] );
506 $this->mNamespaceIds =
null;
513 $this->namespaceNames =
null;
514 $this->mNamespaceIds =
null;
515 $this->namespaceAliases =
null;
526 foreach ( $ns
as $k => $v ) {
527 $ns[$k] = strtr( $v,
'_',
' ' );
545 return isset( $ns[$index] ) ? $ns[$index] :
false;
563 return strtr( $ns,
'_',
' ' );
578 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
580 return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->
getNsText( $index );
600 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
601 return count( $aliases ) > 0;
614 $lctext = $this->
lc( $text );
616 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
623 if ( is_null( $this->namespaceAliases ) ) {
624 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
628 foreach ( $aliases
as $name => $index ) {
630 unset( $aliases[
$name] );
632 $aliases[
$name] = $index;
639 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
640 foreach ( $genders
as $index => $forms ) {
641 foreach ( $forms
as $alias ) {
642 $aliases[$alias] = $index;
646 # Also add converted namespace names as aliases, to avoid confusion.
647 $convertedNames = [];
649 if ( $variant === $this->mCode ) {
653 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
657 $this->namespaceAliases = $aliases + $convertedNames;
667 if ( is_null( $this->mNamespaceIds ) ) {
669 # Put namespace names and aliases into a hashtable.
670 # If this is too slow, then we should arrange it so that it is done
671 # before caching. The catch is that at pre-cache time, the above
672 # class-specific fixup hasn't been done.
673 $this->mNamespaceIds = [];
675 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
678 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
682 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
686 return $this->mNamespaceIds;
697 $lctext = $this->
lc( $text );
698 $ns = MWNamespace::getCanonicalIndex( $lctext );
699 if ( $ns !==
null ) {
703 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
714 $msg =
"variantname-$code";
715 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
720 return $name; #
if it's defined as a language name, show that
722 # otherwise, output the language code
730 public function getDatePreferences() {
731 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
737 function getDateFormats() {
738 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
744 public function getDefaultDateFormat() {
745 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
746 if ( $df === 'dmy
or mdy
' ) {
747 global $wgAmericanDates;
748 return $wgAmericanDates ? 'mdy
' : 'dmy
';
757 public function getDatePreferenceMigrationMap() {
758 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
765 function getImageFile( $image ) {
766 return self::$dataCache->getSubitem( $this->mCode, 'imageFiles
', $image );
773 public function getImageFiles() {
774 return self::$dataCache->getItem( $this->mCode, 'imageFiles
' );
780 public function getExtraUserToggles() {
781 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
788 function getUserToggle( $tog ) {
789 return $this->getMessageFromDB( "tog-$tog" );
803 public static function fetchLanguageNames( $inLanguage = null, $include = 'mw
' ) {
804 $cacheKey = $inLanguage === null ? 'null' : $inLanguage;
805 $cacheKey .= ":$include";
806 if ( self::$languageNameCache === null ) {
807 self::$languageNameCache = new HashBagOStuff( [ 'maxKeys
' => 20 ] );
810 $ret = self::$languageNameCache->get( $cacheKey );
812 $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
813 self::$languageNameCache->set( $cacheKey, $ret );
828 private static function fetchLanguageNamesUncached( $inLanguage = null, $include = 'mw
' ) {
829 global $wgExtraLanguageNames;
831 // If passed an invalid language code to use, fallback to en
832 if ( $inLanguage !== null && !Language::isValidCode( $inLanguage ) ) {
839 # TODO: also include when $inLanguage is null, when this code is more efficient
840 Hooks::run( 'LanguageGetTranslatedLanguageNames
', [ &$names, $inLanguage ] );
843 $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
844 foreach ( $mwNames as $mwCode => $mwName ) {
845 # - Prefer own MediaWiki native name when not using the hook
846 # - For other names just add if not added through the hook
847 if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
848 $names[$mwCode] = $mwName;
852 if ( $include === 'all
' ) {
858 $coreCodes = array_keys( $mwNames );
859 foreach ( $coreCodes as $coreCode ) {
860 $returnMw[$coreCode] = $names[$coreCode];
863 if ( $include === 'mwfile
' ) {
865 # We do this using a foreach over the codes instead of a directory
866 # loop so that messages files in extensions will work correctly.
867 foreach ( $returnMw as $code => $value ) {
868 if ( is_readable( self::getMessagesFileName( $code ) )
869 || is_readable( self::getJsonMessagesFileName( $code ) )
871 $namesMwFile[$code] = $names[$code];
875 ksort( $namesMwFile );
880 # 'mw
' option; default if it's not one
of the
other two
options (all/mwfile)
893 $array = self::fetchLanguageNames( $inLanguage, $include );
894 return !array_key_exists(
$code, $array ) ?
'' : $array[
$code];
904 return $this->
msg( $msg )->text();
913 protected function msg( $msg ) {
914 return wfMessage( $msg )->inLanguage( $this );
929 $monthNames = [
'' ];
930 for ( $i = 1; $i < 13; $i++ ) {
956 $monthNames = [
'' ];
957 for ( $i = 1; $i < 13; $i++ ) {
984 return $this->
getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
992 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1000 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1008 return $this->
getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1020 if ( !$dateTimeObj ) {
1021 $dateTimeObj = DateTime::createFromFormat(
1022 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1025 return $dateTimeObj->format(
$code );
1097 public function sprintfDate( $format, $ts, DateTimeZone $zone =
null, &$ttl =
'unused' ) {
1102 $dateTimeObj =
false;
1111 $usedSecond =
false;
1112 $usedMinute =
false;
1119 $usedISOYear =
false;
1120 $usedIsLeapYear =
false;
1122 $usedHebrewMonth =
false;
1123 $usedIranianMonth =
false;
1124 $usedHijriMonth =
false;
1125 $usedHebrewYear =
false;
1126 $usedIranianYear =
false;
1127 $usedHijriYear =
false;
1128 $usedTennoYear =
false;
1130 if ( strlen( $ts ) !== 14 ) {
1131 throw new MWException( __METHOD__ .
": The timestamp $ts should have 14 characters" );
1134 if ( !ctype_digit( $ts ) ) {
1135 throw new MWException( __METHOD__ .
": The timestamp $ts should be a number" );
1138 $formatLength = strlen( $format );
1139 for ( $p = 0; $p < $formatLength; $p++ ) {
1141 $code = $format[$p];
1142 if (
$code ==
'x' && $p < $formatLength - 1 ) {
1143 $code .= $format[++$p];
1146 if ( (
$code ===
'xi'
1152 && $p < $formatLength - 1 ) {
1153 $code .= $format[++$p];
1164 $rawToggle = !$rawToggle;
1177 $usedHebrewMonth =
true;
1179 $hebrew = self::tsToHebrew( $ts );
1185 $num = substr( $ts, 6, 2 );
1195 $num = intval( substr( $ts, 6, 2 ) );
1200 $iranian = self::tsToIranian( $ts );
1207 $hijri = self::tsToHijri( $ts );
1214 $hebrew = self::tsToHebrew( $ts );
1229 $usedIranianMonth =
true;
1231 $iranian = self::tsToIranian( $ts );
1236 $usedHijriMonth =
true;
1238 $hijri = self::tsToHijri( $ts );
1243 $usedHebrewMonth =
true;
1245 $hebrew = self::tsToHebrew( $ts );
1251 $num = substr( $ts, 4, 2 );
1259 $num = intval( substr( $ts, 4, 2 ) );
1262 $usedIranianMonth =
true;
1264 $iranian = self::tsToIranian( $ts );
1269 $usedHijriMonth =
true;
1271 $hijri = self::tsToHijri( $ts );
1276 $usedHebrewMonth =
true;
1278 $hebrew = self::tsToHebrew( $ts );
1283 $usedHebrewMonth =
true;
1285 $hebrew = self::tsToHebrew( $ts );
1291 $num = substr( $ts, 0, 4 );
1294 $usedIranianYear =
true;
1296 $iranian = self::tsToIranian( $ts );
1301 $usedHijriYear =
true;
1303 $hijri = self::tsToHijri( $ts );
1308 $usedHebrewYear =
true;
1310 $hebrew = self::tsToHebrew( $ts );
1317 $thai = self::tsToYear( $ts,
'thai' );
1324 $minguo = self::tsToYear( $ts,
'minguo' );
1329 $usedTennoYear =
true;
1331 $tenno = self::tsToYear( $ts,
'tenno' );
1337 $num = substr( $ts, 2, 2 );
1340 $usedIranianYear =
true;
1342 $iranian = self::tsToIranian( $ts );
1344 $num = substr( $iranian[0], -2 );
1347 $usedIranianYear =
true;
1349 $iranian = self::tsToIranian( $ts );
1351 $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1354 $usedIranianYear =
true;
1356 $iranian = self::tsToIranian( $ts );
1362 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' :
'pm';
1366 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' :
'PM';
1370 $h = substr( $ts, 8, 2 );
1371 $num = $h % 12 ? $h % 12 : 12;
1375 $num = intval( substr( $ts, 8, 2 ) );
1379 $h = substr( $ts, 8, 2 );
1380 $num = sprintf(
'%02d', $h % 12 ? $h % 12 : 12 );
1384 $num = substr( $ts, 8, 2 );
1388 $num = substr( $ts, 10, 2 );
1392 $num = substr( $ts, 12, 2 );
1419 $usedIsLeapYear =
true;
1423 $usedISOYear =
true;
1434 # Backslash escaping
1435 if ( $p < $formatLength - 1 ) {
1436 $s .= $format[++$p];
1443 if ( $p < $formatLength - 1 ) {
1444 $endQuote = strpos( $format,
'"', $p + 1 );
1445 if ( $endQuote ===
false ) {
1446 # No terminating quote, assume literal "
1449 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1453 # Quote at end of string, assume literal "
1460 if ( $num !==
false ) {
1461 if ( $rawToggle || $raw ) {
1464 } elseif ( $roman ) {
1467 } elseif ( $hebrewNum ) {
1468 $s .= self::hebrewNumeral( $num );
1476 if ( $ttl ===
'unused' ) {
1478 } elseif ( $usedSecond ) {
1480 } elseif ( $usedMinute ) {
1481 $ttl = 60 - substr( $ts, 12, 2 );
1482 } elseif ( $usedHour ) {
1483 $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1484 } elseif ( $usedAMPM ) {
1485 $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 -
1486 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1490 $usedIranianMonth ||
1499 $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1500 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1503 $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1504 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1508 $timeRemainingInDay;
1509 } elseif ( $usedISOYear ) {
1512 $lastWeekOfISOYear = DateTime::createFromFormat(
1514 substr( $ts, 0, 4 ) .
'1228',
1515 $zone ?:
new DateTimeZone(
'UTC' )
1518 $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
1519 $timeRemainingInWeek =
1521 + $timeRemainingInDay;
1522 $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
1528 substr( $ts, 6, 2 ) ) * 86400
1529 + $timeRemainingInDay;
1530 } elseif ( $usedYear ) {
1534 + $timeRemainingInDay;
1535 } elseif ( $usedIsLeapYear ) {
1536 $year = substr( $ts, 0, 4 );
1537 $timeRemainingInYear =
1540 + $timeRemainingInDay;
1542 if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1544 $nextCandidate = $year - $mod + 4;
1545 if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1546 $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1547 $timeRemainingInYear;
1549 $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1550 $timeRemainingInYear;
1554 $possibleTtls[] = $timeRemainingInYear;
1558 if ( $possibleTtls ) {
1559 $ttl = min( $possibleTtls );
1566 private static $GREG_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
1567 private static $IRANIAN_DAYS = [ 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ];
1582 $gy = substr( $ts, 0, 4 ) -1600;
1583 $gm = substr( $ts, 4, 2 ) -1;
1584 $gd = substr( $ts, 6, 2 ) -1;
1586 # Days passed from the beginning (including leap years)
1588 + floor( ( $gy + 3 ) / 4 )
1589 - floor( ( $gy + 99 ) / 100 )
1590 + floor( ( $gy + 399 ) / 400 );
1593 for ( $i = 0; $i < $gm; $i++ ) {
1594 $gDayNo += self::$GREG_DAYS[$i];
1598 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1603 $gDayNo += (int)$gd;
1605 $jDayNo = $gDayNo - 79;
1607 $jNp = floor( $jDayNo / 12053 );
1610 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1613 if ( $jDayNo >= 366 ) {
1614 $jy += floor( ( $jDayNo - 1 ) / 365 );
1615 $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1620 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1621 $jDayNo -= self::$IRANIAN_DAYS[$i];
1627 return [ $jy, $jm, $jd, $jz ];
1642 $year = substr( $ts, 0, 4 );
1643 $month = substr( $ts, 4, 2 );
1644 $day = substr( $ts, 6, 2 );
1652 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1653 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1655 $zjd = (int)( ( 1461 * ( $zy + 4800 + (
int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1656 (
int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1657 (
int)( ( 3 * (int)( ( ( $zy + 4900 + (
int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1660 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (
int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1661 (
int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1664 $zl = $zjd -1948440 + 10632;
1665 $zn = (int)( ( $zl - 1 ) / 10631 );
1666 $zl = $zl - 10631 * $zn + 354;
1667 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
1668 ( (int)( $zl / 5670 ) ) * ( (
int)( ( 43 * $zl ) / 15238 ) );
1669 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
1670 ( (int)( $zj / 16 ) ) * ( (
int)( ( 15238 * $zj ) / 43 ) ) + 29;
1671 $zm = (int)( ( 24 * $zl ) / 709 );
1672 $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1673 $zy = 30 * $zn + $zj - 30;
1675 return [ $zy, $zm, $zd ];
1695 $year = substr( $ts, 0, 4 );
1696 $month = substr( $ts, 4, 2 );
1697 $day = substr( $ts, 6, 2 );
1699 # Calculate Hebrew year
1700 $hebrewYear = $year + 3760;
1702 # Month number when September = 1, August = 12
1704 if ( $month > 12 ) {
1711 # Calculate day of year from 1 September
1713 for ( $i = 1; $i < $month; $i++ ) {
1717 # Check if the year is leap
1718 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1721 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1728 # Calculate the start of the Hebrew year
1729 $start = self::hebrewYearStart( $hebrewYear );
1731 # Calculate next year's start
1732 if ( $dayOfYear <= $start ) {
1733 # Day is before the start of the year - it is the previous year
1735 $nextStart = $start;
1739 # Add days since previous year's 1 September
1741 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1745 # Start of the new (previous) year
1746 $start = self::hebrewYearStart( $hebrewYear );
1749 $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1752 # Calculate Hebrew day of year
1753 $hebrewDayOfYear = $dayOfYear - $start;
1755 # Difference between year's days
1756 $diff = $nextStart - $start;
1757 # Add 12 (or 13 for leap years) days to ignore the difference between
1758 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1759 # difference is only about the year type
1760 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1766 # Check the year pattern, and is leap year
1767 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1768 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1769 # and non-leap years
1770 $yearPattern = $diff % 30;
1771 # Check if leap year
1772 $isLeap = $diff >= 30;
1774 # Calculate day in the month from number of day in the Hebrew year
1775 # Don't check Adar - if the day is not in Adar, we will stop before;
1776 # if it is in Adar, we will use it to check if it is Adar I or Adar II
1777 $hebrewDay = $hebrewDayOfYear;
1780 while ( $hebrewMonth <= 12 ) {
1781 # Calculate days in this month
1782 if ( $isLeap && $hebrewMonth == 6 ) {
1783 # Adar in a leap year
1785 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1787 if ( $hebrewDay <= $days ) {
1791 # Subtract the days of Adar I
1792 $hebrewDay -= $days;
1795 if ( $hebrewDay <= $days ) {
1801 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1802 # Cheshvan in a complete year (otherwise as the rule below)
1804 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1805 # Kislev in an incomplete year (otherwise as the rule below)
1808 # Odd months have 30 days, even have 29
1809 $days = 30 - ( $hebrewMonth - 1 ) % 2;
1811 if ( $hebrewDay <= $days ) {
1812 # In the current month
1815 # Subtract the days of the current month
1816 $hebrewDay -= $days;
1817 # Try in the next month
1822 return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1835 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1836 $b = intval( ( $year - 1 ) % 4 );
1837 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1841 $Mar = intval( $m );
1847 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1848 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1850 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1852 } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1856 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1873 $gy = substr( $ts, 0, 4 );
1874 $gm = substr( $ts, 4, 2 );
1875 $gd = substr( $ts, 6, 2 );
1877 if ( !strcmp( $cName,
'thai' ) ) {
1879 # Add 543 years to the Gregorian calendar
1880 # Months and days are identical
1881 $gy_offset = $gy + 543;
1882 } elseif ( ( !strcmp( $cName,
'minguo' ) ) || !strcmp( $cName,
'juche' ) ) {
1884 # Deduct 1911 years from the Gregorian calendar
1885 # Months and days are identical
1886 $gy_offset = $gy - 1911;
1887 } elseif ( !strcmp( $cName,
'tenno' ) ) {
1888 # Nengō dates up to Meiji period
1889 # Deduct years from the Gregorian calendar
1890 # depending on the nengo periods
1891 # Months and days are identical
1893 || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1894 || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1897 $gy_gannen = $gy - 1868 + 1;
1898 $gy_offset = $gy_gannen;
1899 if ( $gy_gannen == 1 ) {
1902 $gy_offset =
'明治' . $gy_offset;
1904 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1905 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1906 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1907 ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1908 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1911 $gy_gannen = $gy - 1912 + 1;
1912 $gy_offset = $gy_gannen;
1913 if ( $gy_gannen == 1 ) {
1916 $gy_offset =
'大正' . $gy_offset;
1918 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1919 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1920 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1923 $gy_gannen = $gy - 1926 + 1;
1924 $gy_offset = $gy_gannen;
1925 if ( $gy_gannen == 1 ) {
1928 $gy_offset =
'昭和' . $gy_offset;
1931 $gy_gannen = $gy - 1989 + 1;
1932 $gy_offset = $gy_gannen;
1933 if ( $gy_gannen == 1 ) {
1936 $gy_offset =
'平成' . $gy_offset;
1942 return [ $gy_offset, $gm, $gd ];
1959 if ( !preg_match( self::$strongDirRegex, $text,
$matches ) ) {
1977 [
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ],
1978 [
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ],
1979 [
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ],
1980 [
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
1981 'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' ]
1984 $num = intval( $num );
1985 if ( $num > 10000 || $num <= 0 ) {
1990 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1991 if ( $num >= $pow10 ) {
1992 $s .= $table[$i][(int)floor( $num / $pow10 )];
1994 $num = $num % $pow10;
2008 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ],
2009 [
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ],
2022 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ]
2025 $num = intval( $num );
2026 if ( $num > 9999 || $num <= 0 ) {
2031 if ( $num === 1000 ) {
2033 } elseif ( $num % 1000 === 0 ) {
2034 return $table[0][$num / 1000] .
"' אלפים";
2039 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2040 if ( $num >= $pow10 ) {
2041 if ( $num === 15 || $num === 16 ) {
2042 $letters[] = $table[0][9];
2043 $letters[] = $table[0][$num - 9];
2046 $letters = array_merge(
2048 (
array)$table[$i][intval( $num / $pow10 )]
2051 if ( $pow10 === 1000 ) {
2057 $num = $num % $pow10;
2060 $preTransformLength = count( $letters );
2061 if ( $preTransformLength === 1 ) {
2065 $lastIndex = $preTransformLength - 1;
2066 $letters[$lastIndex] = str_replace(
2067 [
'כ',
'מ',
'נ',
'פ',
'צ' ],
2068 [
'ך',
'ם',
'ן',
'ף',
'ץ' ],
2069 $letters[$lastIndex]
2075 if ( $letters[1] ===
"'" && $preTransformLength === 3 ) {
2078 array_splice( $letters, -1, 0,
'"' );
2082 return implode( $letters );
2096 if ( $tz ===
false ) {
2097 $tz =
$wgUser->getOption(
'timecorrection' );
2100 $data = explode(
'|', $tz, 3 );
2102 if ( $data[0] ==
'ZoneInfo' ) {
2104 $userTZ =
new DateTimeZone( $data[2] );
2105 $date =
new DateTime( $ts,
new DateTimeZone(
'UTC' ) );
2106 $date->setTimezone( $userTZ );
2107 return $date->format(
'YmdHis' );
2108 }
catch ( Exception
$e ) {
2110 $data[0] =
'Offset';
2114 if ( $data[0] ==
'System' || $tz ==
'' ) {
2115 # Global offset in minutes.
2117 } elseif ( $data[0] ==
'Offset' ) {
2118 $minDiff = intval( $data[1] );
2120 $data = explode(
':', $tz );
2121 if ( count( $data ) == 2 ) {
2122 $data[0] = intval( $data[0] );
2123 $data[1] = intval( $data[1] );
2124 $minDiff = abs( $data[0] ) * 60 + $data[1];
2125 if ( $data[0] < 0 ) {
2126 $minDiff = -$minDiff;
2129 $minDiff = intval( $data[0] ) * 60;
2133 # No difference ? Return time unchanged
2134 if ( 0 == $minDiff ) {
2138 MediaWiki\suppressWarnings();
2139 # Generate an adjusted date; take advantage of the fact that mktime
2140 # will normalize out-of-range values so we don't have to split $minDiff
2141 # into hours and minutes.
2143 (
int)substr( $ts, 8, 2 ) ), # Hours
2144 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2145 (
int)substr( $ts, 12, 2 ), # Seconds
2146 (
int)substr( $ts, 4, 2 ), # Month
2147 (
int)substr( $ts, 6, 2 ), # Day
2148 (
int)substr( $ts, 0, 4 ) ); # Year
2150 $date =
date(
'YmdHis',
$t );
2151 MediaWiki\restoreWarnings();
2174 if ( is_bool( $usePrefs ) ) {
2176 $datePreference =
$wgUser->getDatePreference();
2178 $datePreference = (
string)User::getDefaultOption(
'date' );
2181 $datePreference = (
string)$usePrefs;
2185 if ( $datePreference ==
'' ) {
2189 return $datePreference;
2203 $wasDefault =
false;
2204 if ( $pref ==
'default' ) {
2209 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2210 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2212 if (
$type ===
'pretty' && $df ===
null ) {
2216 if ( !$wasDefault && $df ===
null ) {
2218 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2221 $this->dateFormatStrings[
$type][$pref] = $df;
2223 return $this->dateFormatStrings[
$type][$pref];
2236 public function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2239 $ts = $this->
userAdjust( $ts, $timecorrection );
2255 public function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2258 $ts = $this->
userAdjust( $ts, $timecorrection );
2275 public function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2278 $ts = $this->
userAdjust( $ts, $timecorrection );
2299 foreach ( $intervals
as $intervalName => $intervalValue ) {
2302 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2303 $segments[] = $message->inLanguage( $this )->escaped();
2321 if ( empty( $chosenIntervals ) ) {
2322 $chosenIntervals = [
2334 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2335 $sortedNames = array_keys( $intervals );
2336 $smallestInterval = array_pop( $sortedNames );
2340 foreach ( $intervals
as $name => $length ) {
2341 $value = floor( $seconds / $length );
2343 if (
$value > 0 || (
$name == $smallestInterval && empty( $segments ) ) ) {
2344 $seconds -=
$value * $length;
2373 $options += [
'timecorrection' =>
true,
'format' =>
true ];
2374 if (
$options[
'timecorrection'] !==
false ) {
2375 if (
$options[
'timecorrection'] ===
true ) {
2376 $offset =
$user->getOption(
'timecorrection' );
2378 $offset =
$options[
'timecorrection'];
2382 if (
$options[
'format'] ===
true ) {
2383 $format =
$user->getDatePreference();
2478 if ( $relativeTo ===
null ) {
2481 if (
$user ===
null ) {
2487 $offsetRel = $relativeTo->offsetForUser(
$user );
2490 if ( Hooks::run(
'GetHumanTimestamp', [ &$ts,
$time, $relativeTo,
$user, $this ] ) ) {
2495 $time->timestamp->sub( $offsetThis );
2496 $relativeTo->timestamp->sub( $offsetRel );
2515 $diff = $ts->diff( $relativeTo );
2516 $diffDay = (bool)( (
int)$ts->timestamp->format(
'w' ) -
2517 (int)$relativeTo->timestamp->format(
'w' ) );
2518 $days = $diff->days ?: (int)$diffDay;
2519 if ( $diff->invert || $days > 5
2520 && $ts->timestamp->format(
'Y' ) !== $relativeTo->timestamp->format(
'Y' )
2528 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2529 } elseif ( $days > 5 ) {
2532 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2533 } elseif ( $days > 1 ) {
2536 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2540 ->inLanguage( $this )
2541 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2543 } elseif ( $days == 1 ) {
2547 ->inLanguage( $this )
2548 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2550 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2554 ->inLanguage( $this )
2555 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2560 } elseif ( $diff->h == 1 ) {
2562 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2563 } elseif ( $diff->i >= 1 ) {
2565 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2566 } elseif ( $diff->s >= 30 ) {
2568 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2582 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2589 return self::$dataCache->getItem( $this->mCode,
'messages' );
2600 # *input* string. We just ignore those too.
2603 MediaWiki\suppressWarnings();
2604 $text =
iconv( $in,
$out .
'//IGNORE', $string );
2605 MediaWiki\restoreWarnings();
2624 return mb_strtoupper(
$matches[0] );
2632 return mb_strtoupper(
$matches[0] );
2646 } elseif ( $o < 128 ) {
2650 return $this->
uc( $str,
true );
2662 public function uc( $str, $first =
false ) {
2665 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2670 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2681 return strval( $str );
2682 } elseif ( $o >= 128 ) {
2683 return $this->
lc( $str,
true );
2684 } elseif ( $o > 96 ) {
2687 $str[0] = strtolower( $str[0] );
2697 function lc( $str, $first =
false ) {
2700 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2702 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2705 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2714 return strlen( $str ) !== mb_strlen( $str );
2723 $str = $this->
lc( $str );
2726 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2729 return preg_replace_callback(
2731 [ $this,
'ucwordsCallbackMB' ],
2735 return ucwords( strtolower( $str ) );
2747 $str = $this->
lc( $str );
2750 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2753 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2754 "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2756 return preg_replace_callback(
2758 [ $this,
'ucwordbreaksCallbackMB' ],
2762 return preg_replace_callback(
2763 '/\b([\w\x80-\xff]+)\b/',
2764 [ $this,
'ucwordbreaksCallbackAscii' ],
2786 return $this->
uc(
$s );
2795 if ( is_array(
$s ) ) {
2796 throw new MWException(
'Given array to checkTitleEncoding.' );
2809 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2843 return self::convertDoubleWidth( $string );
2855 static $full =
null;
2856 static $half =
null;
2858 if ( $full ===
null ) {
2859 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2860 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2861 $full = str_split( $fullWidth, 3 );
2862 $half = str_split( $halfWidth );
2865 $string = str_replace( $full, $half, $string );
2875 $string = preg_replace( $pattern,
" $1 ", $string );
2876 $string = preg_replace(
'/ +/',
' ', $string );
2885 # some languages, e.g. Chinese, need to do a conversion
2886 # in order for search results to be displayed correctly
2899 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2900 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2906 if ( strlen(
$matches[1] ) != 3 ) {
2914 } elseif (
$code < 0xb098 ) {
2915 return "\xe3\x84\xb1";
2916 } elseif (
$code < 0xb2e4 ) {
2917 return "\xe3\x84\xb4";
2918 } elseif (
$code < 0xb77c ) {
2919 return "\xe3\x84\xb7";
2920 } elseif (
$code < 0xb9c8 ) {
2921 return "\xe3\x84\xb9";
2922 } elseif (
$code < 0xbc14 ) {
2923 return "\xe3\x85\x81";
2924 } elseif (
$code < 0xc0ac ) {
2925 return "\xe3\x85\x82";
2926 } elseif (
$code < 0xc544 ) {
2927 return "\xe3\x85\x85";
2928 } elseif (
$code < 0xc790 ) {
2929 return "\xe3\x85\x87";
2930 } elseif (
$code < 0xcc28 ) {
2931 return "\xe3\x85\x88";
2932 } elseif (
$code < 0xce74 ) {
2933 return "\xe3\x85\x8a";
2934 } elseif (
$code < 0xd0c0 ) {
2935 return "\xe3\x85\x8b";
2936 } elseif (
$code < 0xd30c ) {
2937 return "\xe3\x85\x8c";
2938 } elseif (
$code < 0xd558 ) {
2939 return "\xe3\x85\x8d";
2941 return "\xe3\x85\x8e";
2986 $s = UtfNormal\Validator::cleanUp(
$s );
3010 if ( !isset( $this->transformData[$file] ) ) {
3012 if ( $data ===
false ) {
3013 throw new MWException( __METHOD__ .
": The transformation file $file is missing" );
3017 return $this->transformData[$file]->replace( $string );
3026 return self::$dataCache->getItem( $this->mCode,
'rtl' );
3034 return $this->
isRTL() ?
'rtl' :
'ltr';
3046 return $this->
isRTL() ?
'right' :
'left';
3058 return $this->
isRTL() ?
'left' :
'right';
3074 return $this->
isRTL() ?
'‎' :
'‏';
3076 return $this->
isRTL() ?
'‏' :
'‎';
3090 $lrm =
"\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3091 $rlm =
"\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3093 return $this->
isRTL() ? $lrm : $rlm;
3095 return $this->
isRTL() ? $rlm : $lrm;
3102 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
3113 switch ( $direction ) {
3115 return $this->
isRTL() ?
'←' :
'→';
3117 return $this->
isRTL() ?
'→' :
'←';
3135 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
3143 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
3150 if ( $this->mMagicHookDone ) {
3153 $this->mMagicHookDone =
true;
3154 Hooks::run(
'LanguageGetMagic', [ &$this->mMagicExtensions, $this->
getCode() ] );
3164 if ( !$this->mMagicHookDone ) {
3168 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3169 $rawEntry = $this->mMagicExtensions[$mw->mId];
3171 $rawEntry = self::$dataCache->getSubitem(
3172 $this->mCode,
'magicWords', $mw->mId );
3175 if ( !is_array( $rawEntry ) ) {
3176 wfWarn(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3178 $mw->mCaseSensitive = $rawEntry[0];
3179 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3190 $fallbackChain = array_reverse( $fallbackChain );
3191 foreach ( $fallbackChain
as $code ) {
3192 if ( isset( $newWords[
$code] ) ) {
3193 $this->mMagicExtensions = $newWords[
$code] + $this->mMagicExtensions;
3205 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3207 $this->mExtendedSpecialPageAliases =
3208 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3209 Hooks::run(
'LanguageGetSpecialPageAliases',
3210 [ &$this->mExtendedSpecialPageAliases, $this->
getCode() ] );
3213 return $this->mExtendedSpecialPageAliases;
3223 return "<em>$text</em>";
3250 if ( !$nocommafy ) {
3251 $number = $this->
commafy( $number );
3254 $number = strtr( $number,
$s );
3261 $number = strtr( $number,
$s );
3277 return $this->
formatNum( $number,
true );
3288 $s = array_filter(
$s );
3289 $number = strtr( $number, array_flip(
$s ) );
3295 $s = array_filter(
$s );
3296 $number = strtr( $number, array_flip(
$s ) );
3299 $number = strtr( $number, [
',' =>
'' ] );
3311 if ( $number ===
null ) {
3317 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3321 if ( intval( $number ) < 0 ) {
3324 $number = substr( $number, 1 );
3329 preg_match(
"/\d+/", $number, $integerPart );
3330 preg_match(
"/\.\d*/", $number, $decimalPart );
3331 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3332 if ( $groupedNumber === $number ) {
3334 return $sign . $groupedNumber;
3336 $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3337 while ( $start > 0 ) {
3338 $match =
$matches[0][$numMatches - 1];
3339 $matchLen = strlen( $match );
3340 $start = $end - $matchLen;
3344 $groupedNumber = substr( $number, $start, $end -$start ) . $groupedNumber;
3346 if ( $numMatches > 1 ) {
3351 $groupedNumber =
"," . $groupedNumber;
3354 return $sign . $groupedNumber;
3362 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3369 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3376 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3389 $m = count( $l ) - 1;
3394 $and = $this->
msg(
'and' )->escaped();
3395 $space = $this->
msg(
'word-separator' )->escaped();
3397 $comma = $this->
msg(
'comma-separator' )->escaped();
3401 for ( $i = $m - 1; $i >= 0; $i-- ) {
3402 if ( $i == $m - 1 ) {
3403 $s = $l[$i] . $and . $space .
$s;
3405 $s = $l[$i] . $comma .
$s;
3419 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3432 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3444 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3466 function truncate( $string, $length, $ellipsis =
'...', $adjustLength =
true ) {
3467 # Use the localized ellipsis character
3468 if ( $ellipsis ==
'...' ) {
3469 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3471 # Check if there is no need to truncate
3472 if ( $length == 0 ) {
3474 } elseif ( strlen( $string ) <= abs( $length ) ) {
3477 $stringOriginal = $string;
3478 # If ellipsis length is >= $length then we can't apply $adjustLength
3479 if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
3480 $string = $ellipsis;
3481 # Otherwise, truncate and add ellipsis...
3483 $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
3484 if ( $length > 0 ) {
3485 $length -= $eLength;
3486 $string = substr( $string, 0, $length );
3488 $string = rtrim( $string );
3489 $string = $string . $ellipsis;
3491 $length += $eLength;
3492 $string = substr( $string, $length );
3494 $string = ltrim( $string );
3495 $string = $ellipsis . $string;
3498 # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3499 # This check is *not* redundant if $adjustLength, due to the single case where
3500 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3501 if ( strlen( $string ) < strlen( $stringOriginal ) ) {
3504 return $stringOriginal;
3516 if ( $string !=
'' ) {
3517 $char = ord( $string[strlen( $string ) - 1] );
3519 if ( $char >= 0xc0 ) {
3520 # We got the first byte only of a multibyte char; remove it.
3521 $string = substr( $string, 0, -1 );
3522 } elseif ( $char >= 0x80 &&
3524 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3525 '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3527 # We chopped in the middle of a character; remove it
3542 if ( $string !=
'' ) {
3543 $char = ord( $string[0] );
3544 if ( $char >= 0x80 && $char < 0xc0 ) {
3545 # We chopped in the middle of a character; remove the whole thing
3546 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3568 # Use the localized ellipsis character
3569 if ( $ellipsis ==
'...' ) {
3570 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3572 # Check if there is clearly no need to truncate
3573 if ( $length <= 0 ) {
3575 } elseif ( strlen( $text ) <= $length ) {
3580 $testingEllipsis =
false;
3588 $textLen = strlen( $text );
3589 $neLength = max( 0, $length - strlen( $ellipsis ) );
3590 for ( $pos = 0;
true; ++$pos ) {
3591 # Consider truncation once the display length has reached the maximim.
3592 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3593 # Check that we're not in the middle of a bracket/entity...
3594 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3595 if ( !$testingEllipsis ) {
3596 $testingEllipsis =
true;
3597 # Save where we are; we will truncate here unless there turn out to
3598 # be so few remaining characters that truncation is not necessary.
3599 if ( !$maybeState ) {
3600 $maybeState = [
$ret, $openTags ];
3602 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3603 # String in fact does need truncation, the truncation point was OK.
3604 list(
$ret, $openTags ) = $maybeState;
3610 if ( $pos >= $textLen ) {
3614 # Read the next char...
3616 $lastCh = $pos ? $text[$pos - 1] :
'';
3622 } elseif ( $ch ==
'>' ) {
3626 } elseif ( $bracketState == 1 ) {
3634 } elseif ( $bracketState == 2 ) {
3641 } elseif ( $bracketState == 0 ) {
3642 if ( $entityState ) {
3648 if ( $neLength == 0 && !$maybeState ) {
3651 $maybeState = [ substr(
$ret, 0, -1 ), $openTags ];
3658 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3660 $dispLen += $skipped;
3668 while ( count( $openTags ) > 0 ) {
3669 $ret .=
'</' . array_pop( $openTags ) .
'>';
3686 if ( $len ===
null ) {
3688 } elseif ( $len < 0 ) {
3692 if ( $start < strlen( $text ) ) {
3693 $skipCount = strcspn( $text, $search, $start, $len );
3694 $ret .= substr( $text, $start, $skipCount );
3711 if ( $tagType == 0 && $lastCh !=
'/' ) {
3713 } elseif ( $tagType == 1 ) {
3714 if ( $openTags &&
$tag == $openTags[count( $openTags ) - 1] ) {
3715 array_pop( $openTags );
3744 if ( is_string( $forms ) ) {
3748 foreach ( array_values( $forms )
as $rule ) {
3751 if ( $form ===
'@metadata' ) {
3755 $replacement = $rule[1];
3757 $regex =
'/' . addcslashes( $form,
'/' ) .
'/u';
3758 $patternMatches = preg_match( $regex, $word );
3760 if ( $patternMatches ===
false ) {
3762 'An error occurred while processing grammar. ' .
3763 "Word: '$word'. Regex: /$form/."
3765 } elseif ( $patternMatches === 1 ) {
3766 $word = preg_replace( $regex, $replacement, $word );
3802 $languageCode = $this->
getCode();
3804 if ( self::$grammarTransformations ===
null ) {
3805 self::$grammarTransformations =
new MapCacheLRU( 10 );
3808 if ( self::$grammarTransformations->has( $languageCode ) ) {
3809 return self::$grammarTransformations->get( $languageCode );
3814 $grammarDataFile = __DIR__ .
"/data/grammarTransformations/$languageCode.json";
3815 if ( is_readable( $grammarDataFile ) ) {
3816 $data = FormatJson::decode(
3817 file_get_contents( $grammarDataFile ),
3821 if ( $data ===
null ) {
3822 throw new MWException(
"Invalid grammar data for \"$languageCode\"." );
3825 self::$grammarTransformations->set( $languageCode, $data );
3851 if ( !count( $forms ) ) {
3855 if ( $gender ===
'male' ) {
3858 if ( $gender ===
'female' ) {
3861 return isset( $forms[2] ) ? $forms[2] : $forms[0];
3882 if ( is_string( $forms ) ) {
3885 if ( !count( $forms ) ) {
3890 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
3891 return $forms[$pluralForm];
3910 foreach ( $forms
as $index => $form ) {
3911 if ( preg_match(
'/\d+=/i', $form ) ) {
3912 $pos = strpos( $form,
'=' );
3913 if ( substr( $form, 0, $pos ) === (
string)$count ) {
3914 return substr( $form, $pos + 1 );
3916 unset( $forms[$index] );
3919 return array_values( $forms );
3931 while ( count( $forms ) < $count ) {
3932 $forms[] = $forms[count( $forms ) - 1];
3955 if ( $dir ===
'ltr' ) {
3957 return self::$lre . $text . self::$pdf;
3959 if ( $dir ===
'rtl' ) {
3961 return self::$rle . $text . self::$pdf;
3982 foreach ( $duration
as $show =>
$value ) {
3983 if ( strcmp( $str,
$value ) == 0 ) {
3984 return htmlspecialchars( trim( $show ) );
3989 foreach ( $duration
as $show =>
$value ) {
3991 return htmlspecialchars( trim( $show ) );
3997 $time = strtotime( $str, $now );
3998 if (
$time ===
false ) {
4000 } elseif (
$time !== strtotime( $str, $now + 1 ) ) {
4005 if (
$time === 0 ) {
4007 $time =
'19700101000000';
4044 return $this->mConverter;
4054 return $this->mConverter->autoConvertToAllVariants( $text );
4064 return $this->mConverter->convert( $text );
4074 return $this->mConverter->convertTitle(
$title );
4084 return $this->mConverter->convertNamespace( $ns );
4104 return (
bool)$this->mConverter->validateVariant( $variant );
4115 return htmlspecialchars( $this->
convert( $text, $isTitle ) );
4123 return $this->mConverter->convertCategoryKey( $key );
4133 return $this->mConverter->getVariants();
4140 return $this->mConverter->getPreferredVariant();
4147 return $this->mConverter->getDefaultVariant();
4154 return $this->mConverter->getURLVariant();
4170 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
4180 return $this->mConverter->getExtraHashOptions();
4191 return $this->mConverter->getParsedTitle();
4201 $this->mConverter->updateConversionTable(
$title );
4219 return $this->mConverter->markNoConversion( $text );
4232 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
4242 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
4253 if ( $this->mParentLanguage !==
false ) {
4254 return $this->mParentLanguage;
4259 $this->mParentLanguage =
null;
4263 if ( !
$lang->hasVariant( $this->getCode() ) ) {
4264 $this->mParentLanguage =
null;
4268 $this->mParentLanguage =
$lang;
4280 return $lang->getCode() === $this->mCode;
4292 return $this->mCode;
4306 if ( is_null( $this->mHtmlCode ) ) {
4309 return $this->mHtmlCode;
4316 $this->mCode =
$code;
4318 $this->mHtmlCode =
null;
4319 $this->mParentLanguage =
false;
4331 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4332 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4333 if ( !count( $m ) ) {
4336 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4344 if (
$code ==
'en' ) {
4347 return 'Language' . str_replace(
'-',
'_',
ucfirst(
$code ) );
4360 if ( !self::isValidBuiltInCode(
$code ) ) {
4361 throw new MWException(
"Invalid language code \"$code\"" );
4364 return $prefix . str_replace(
'-',
'_',
ucfirst(
$code ) ) . $suffix;
4373 $file = self::getFileName(
"$IP/languages/messages/Messages",
$code,
'.php' );
4374 Hooks::run(
'Language::getMessagesFileName', [
$code, &$file ] );
4387 if ( !self::isValidBuiltInCode(
$code ) ) {
4388 throw new MWException(
"Invalid language code \"$code\"" );
4391 return "$IP/languages/i18n/$code.json";
4402 $fallbacks = self::getFallbacksFor(
$code );
4404 return $fallbacks[0];
4422 return self::getLocalisationCache()->getItem(
$code,
'fallbackSequence' ) ?: [
'en' ];
4438 $cacheKey =
"{$code}-{$wgLanguageCode}";
4440 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4441 $fallbacks = self::getFallbacksFor(
$code );
4448 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4450 self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4452 return self::$fallbackLanguageCache[$cacheKey];
4465 return self::getLocalisationCache()->getItem(
$code,
'messages' );
4477 return self::getLocalisationCache()->getSubitem(
$code,
'messages', $key );
4489 return self::getLocalisationCache()->getSubitemList(
$code,
'messages' );
4497 if ( strpos( $talk,
'$1' ) ===
false ) {
4504 # Allow grammar transformations
4505 # Allowing full message-style parsing would make simple requests
4506 # such as action=raw much more expensive than they need to be.
4507 # This will hopefully cover most cases.
4508 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4509 [ $this,
'replaceGrammarInNamespace' ], $talk );
4510 return str_replace(
' ',
'_', $talk );
4531 public function formatExpiry( $expiry, $format =
true, $infinity =
'infinity' ) {
4533 if ( $dbInfinity ===
null ) {
4537 if ( $expiry ==
'' || $expiry ===
'infinity' || $expiry == $dbInfinity ) {
4538 return $format ===
true
4542 return $format ===
true
4562 if ( !is_array( $format ) ) {
4563 $format = [
'avoid' => $format ];
4565 if ( !isset( $format[
'avoid'] ) ) {
4566 $format[
'avoid'] =
false;
4568 if ( !isset( $format[
'noabbrevs'] ) ) {
4569 $format[
'noabbrevs'] =
false;
4572 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4574 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4576 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4578 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4580 if ( round( $seconds * 10 ) < 100 ) {
4581 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4582 $s = $secondsMsg->params(
$s )->text();
4583 } elseif ( round( $seconds ) < 60 ) {
4585 $s = $secondsMsg->params(
$s )->text();
4586 } elseif ( round( $seconds ) < 3600 ) {
4587 $minutes = floor( $seconds / 60 );
4588 $secondsPart = round( fmod( $seconds, 60 ) );
4589 if ( $secondsPart == 60 ) {
4593 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4595 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4596 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4597 $hours = floor( $seconds / 3600 );
4598 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4599 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4600 if ( $secondsPart == 60 ) {
4604 if ( $minutes == 60 ) {
4608 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4610 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4611 if ( !in_array( $format[
'avoid'], [
'avoidseconds',
'avoidminutes' ] ) ) {
4612 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4615 $days = floor( $seconds / 86400 );
4616 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4617 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4618 if ( $hours == 24 ) {
4622 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4624 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4625 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4626 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4627 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4628 if ( $minutes == 60 ) {
4632 if ( $hours == 24 ) {
4636 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4638 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4640 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4642 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4673 return str_replace(
'$1', $this->
formatNum( $size ),
4677 $sizes = [
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' ];
4680 $maxIndex = count( $sizes ) - 1;
4681 while ( $size >= $boundary && $index < $maxIndex ) {
4692 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4694 $size = round( $size, $round );
4696 return str_replace(
'$1', $this->
formatNum( $size ), $text );
4727 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) . $this->getDirMark();
4731 $this->
msg(
'word-separator' )->escaped() .
4732 $this->
msg(
'parentheses' )->rawParams( $details )->escaped();
4750 # Make 'previous' link
4752 if ( $offset > 0 ) {
4754 $query, $prev,
'prevn-title',
'mw-prevlink' );
4756 $plink = htmlspecialchars( $prev );
4762 $nlink = htmlspecialchars( $next );
4765 $query, $next,
'nextn-title',
'mw-nextlink' );
4768 # Make links to set number of items per page
4770 foreach ( [ 20, 50, 100, 250, 500 ]
as $num ) {
4775 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title(
$title
4776 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
4795 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title(
$title )
4796 ->numParams(
$limit )->text();
4798 return Html::element(
'a', [
'href' =>
$title->getLocalURL(
$query ),
4799 'title' => $tooltip,
'class' => $class ],
$link );
4808 return $this->mConverter->getConvRuleTitle();
4817 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
4819 if ( !$pluralRules ) {
4820 foreach ( $fallbacks
as $fallbackCode ) {
4821 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
4822 if ( $pluralRules ) {
4827 return $pluralRules;
4836 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
4838 if ( !$pluralRules ) {
4839 foreach ( $fallbacks
as $fallbackCode ) {
4840 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
4841 if ( $pluralRules ) {
4846 return $pluralRules;
4855 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
4857 if ( !$pluralRuleTypes ) {
4858 foreach ( $fallbacks
as $fallbackCode ) {
4859 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
4860 if ( $pluralRuleTypes ) {
4865 return $pluralRuleTypes;
4875 $form = Evaluator::evaluateCompiled( $number, $pluralRules );
4890 if ( isset( $pluralRuleTypes[$index] ) ) {
4891 return $pluralRuleTypes[$index];
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.
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
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.
A fake language converter.
Simple store for keeping values in an associative array for the current process.
Base class for language conversion.
static array $languagesWithVariants
languages supporting variants
Internationalisation code.
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.
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
static getFallbacksFor( $code)
Get the ordered list of fallback languages.
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)
Check if the language has the specific variant.
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)
static fetchLanguageName( $code, $inLanguage=null, $include='all')
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()
static getFileName( $prefix='Language', $code, $suffix='.php')
Get the name of a file for a certain language code.
ucwordbreaksCallbackAscii( $matches)
static LocalisationCache $dataCache
listToText(array $l)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
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.
doMagicHook()
Run the LanguageGetMagic hook once.
$mExtendedSpecialPageAliases
getGrammarForms()
Get the grammar forms for the content language.
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...
LanguageConverter $mConverter
caseFold( $s)
Return a case-folded representation of $s.
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.
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
getMonthAbbreviationsArray()
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.
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".
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
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
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
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...
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
convertNamespace( $ns)
Convert a namespace index to a string in the preferred variant.
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.
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)
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
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...
convertHtml( $text, $isTitle=false)
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 newFromCode( $code)
Create a language object for a given language code.
static romanNumeral( $num)
Roman number formatting up to 10000.
static classFromCode( $code)
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 getMain()
Static methods.
static getSuggestedDurations( $lang=null)
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,...
=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
when a variable name is used in a it is silently declared as a new local masking the global
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
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
the array() calling protocol came about after MediaWiki 1.4rc1.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
see documentation in includes Linker php for Linker::makeImageLink & $time
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
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 RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers please use GetContentModels hook to make them known to core if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
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
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub 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
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books $tag
namespace and then decline to actually register it file or subcat img or subcat $title
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 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
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
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
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
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub 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
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
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
if(!isset( $args[0])) $lang