29use CLDRPluralRuleParser\Evaluator;
96 'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
101 'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
105 'january',
'february',
'march',
'april',
'may_long',
'june',
106 'july',
'august',
'september',
'october',
'november',
110 'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
111 'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
115 'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
116 'sep',
'oct',
'nov',
'dec'
120 'iranian-calendar-m1',
'iranian-calendar-m2',
'iranian-calendar-m3',
121 'iranian-calendar-m4',
'iranian-calendar-m5',
'iranian-calendar-m6',
122 'iranian-calendar-m7',
'iranian-calendar-m8',
'iranian-calendar-m9',
123 'iranian-calendar-m10',
'iranian-calendar-m11',
'iranian-calendar-m12'
127 'hebrew-calendar-m1',
'hebrew-calendar-m2',
'hebrew-calendar-m3',
128 'hebrew-calendar-m4',
'hebrew-calendar-m5',
'hebrew-calendar-m6',
129 'hebrew-calendar-m7',
'hebrew-calendar-m8',
'hebrew-calendar-m9',
130 'hebrew-calendar-m10',
'hebrew-calendar-m11',
'hebrew-calendar-m12',
131 'hebrew-calendar-m6a',
'hebrew-calendar-m6b'
135 'hebrew-calendar-m1-gen',
'hebrew-calendar-m2-gen',
'hebrew-calendar-m3-gen',
136 'hebrew-calendar-m4-gen',
'hebrew-calendar-m5-gen',
'hebrew-calendar-m6-gen',
137 'hebrew-calendar-m7-gen',
'hebrew-calendar-m8-gen',
'hebrew-calendar-m9-gen',
138 'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
139 'hebrew-calendar-m6a-gen',
'hebrew-calendar-m6b-gen'
143 'hijri-calendar-m1',
'hijri-calendar-m2',
'hijri-calendar-m3',
144 'hijri-calendar-m4',
'hijri-calendar-m5',
'hijri-calendar-m6',
145 'hijri-calendar-m7',
'hijri-calendar-m8',
'hijri-calendar-m9',
146 'hijri-calendar-m10',
'hijri-calendar-m11',
'hijri-calendar-m12'
153 static public $durationIntervals = [
154 'millennia' => 31556952000,
155 'centuries' => 3155695200,
156 'decades' => 315569520,
171 static private $fallbackLanguageCache = [];
188 static private $lre =
"\u{202A}";
189 static private $rle =
"\u{202B}";
190 static private $pdf =
"\u{202C}";
205 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';
222 $langObj = self::$mLangObjCache[
$code] ?? self::newFromCode(
$code );
225 self::$mLangObjCache = array_merge( [
$code => $langObj ], self::$mLangObjCache );
240 if ( !self::isValidCode(
$code ) ) {
241 throw new MWException(
"Invalid language code \"$code\"" );
244 if ( !self::isValidBuiltInCode(
$code ) ) {
255 if ( class_exists( $class ) && is_a( $class,
'Language',
true ) ) {
261 $fallbacks = self::getFallbacksFor(
$code );
262 foreach ( $fallbacks as $fallbackCode ) {
263 if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
264 throw new MWException(
"Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
267 $class = self::classFromCode( $fallbackCode );
268 if ( class_exists( $class ) ) {
275 throw new MWException(
"Invalid fallback sequence for language '$code'" );
284 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
285 throw new MWException( __METHOD__ .
' must not be used outside tests' );
287 self::$dataCache =
null;
289 self::getLocalisationCache();
290 self::$mLangObjCache = [];
291 self::$fallbackLanguageCache = [];
292 self::$grammarTransformations =
null;
293 self::$languageNameCache =
null;
305 if ( !self::isValidBuiltInCode(
$code ) ) {
309 if (
$code ===
'qqq' ) {
313 return is_readable( self::getMessagesFileName(
$code ) ) ||
314 is_readable( self::getJsonMessagesFileName(
$code ) );
335 $alphanum =
'[a-z0-9]';
336 $x =
'x'; #
private use singleton
337 $singleton =
'[a-wy-z]'; # other singleton
338 $s = $lenient ?
'[-_]' :
'-';
340 $language =
"$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
341 $script =
"$alpha{4}"; # ISO 15924
342 $region =
"(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2 or UN M.49
343 $variant =
"(?:$alphanum{5,8}|$digit$alphanum{3})";
344 $extension =
"$singleton(?:$s$alphanum{2,8})+";
345 $privateUse =
"$x(?:$s$alphanum{1,8})+";
347 # Define certain grandfathered codes, since otherwise the regex is pretty useless.
348 # Since these are limited, this is safe even later changes to the registry --
349 # the only oddity is that it might change the type of the tag, and thus
350 # the results from the capturing groups.
353 $grandfathered =
"en{$s}GB{$s}oed"
354 .
"|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
355 .
"|no{$s}(?:bok|nyn)"
356 .
"|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
357 .
"|zh{$s}min{$s}nan";
359 $variantList =
"$variant(?:$s$variant)*";
360 $extensionList =
"$extension(?:$s$extension)*";
362 $langtag =
"(?:($language)"
365 .
"(?:$s$variantList)?"
366 .
"(?:$s$extensionList)?"
367 .
"(?:$s$privateUse)?)";
369 # The final breakdown, with capturing groups for each of these components
370 # The variants, extensions, grandfathered, and private-use may have interior '-'
372 $root =
"^(?:$langtag|$privateUse|$grandfathered)$";
374 return (
bool)preg_match(
"/$root/", strtolower(
$code ) );
395 strcspn(
$code,
":/\\\000&<>'\"" ) === strlen(
$code )
412 if ( !is_string(
$code ) ) {
413 if ( is_object(
$code ) ) {
414 $addmsg =
" of class " . get_class(
$code );
419 throw new MWException( __METHOD__ .
" must be passed a string, $type given$addmsg" );
422 return (
bool)preg_match(
'/^[a-z0-9-]{2,}$/',
$code );
436 if ( !self::isValidBuiltInCode( $tag ) ) {
441 || self::fetchLanguageName( $tag, $tag ) !==
''
455 if ( is_null( self::$dataCache ) ) {
460 return self::$dataCache;
466 if ( static::class ===
'Language' ) {
469 $this->mCode = str_replace(
'_',
'-', strtolower( substr( static::class, 8 ) ) );
471 self::getLocalisationCache();
478 foreach ( $this as $name =>
$value ) {
479 unset( $this->$name );
495 return self::getFallbacksFor( $this->mCode );
503 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
513 if ( is_null( $this->namespaceNames ) ) {
516 $validNamespaces = MWNamespace::getCanonicalNamespaces();
519 self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
520 $this->namespaceNames += $validNamespaces;
531 # Sometimes a language will be localised but not actually exist on this wiki.
532 foreach ( $this->namespaceNames as $key => $text ) {
533 if ( !isset( $validNamespaces[$key] ) ) {
534 unset( $this->namespaceNames[$key] );
538 # The above mixing may leave namespaces out of canonical order.
539 # Re-order by namespace ID number...
540 ksort( $this->namespaceNames );
542 Hooks::run(
'LanguageGetNamespaces', [ &$this->namespaceNames ] );
554 $this->mNamespaceIds =
null;
561 $this->namespaceNames =
null;
562 $this->mNamespaceIds =
null;
563 $this->namespaceAliases =
null;
574 foreach ( $ns as $k => $v ) {
575 $ns[$k] = strtr( $v,
'_',
' ' );
593 return $ns[$index] ??
false;
611 return strtr( $ns,
'_',
' ' );
626 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
628 return $ns[$index][$gender] ?? $this->
getNsText( $index );
648 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
649 return count( $aliases ) > 0;
662 $lctext = $this->
lc( $text );
664 return $ids[$lctext] ??
false;
671 if ( is_null( $this->namespaceAliases ) ) {
672 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
676 foreach ( $aliases as $name => $index ) {
678 unset( $aliases[$name] );
680 $aliases[
$name] = $index;
687 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
688 foreach ( $genders as $index => $forms ) {
689 foreach ( $forms as $alias ) {
690 $aliases[$alias] = $index;
694 # Also add converted namespace names as aliases, to avoid confusion.
695 $convertedNames = [];
697 if ( $variant === $this->mCode ) {
701 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
705 $this->namespaceAliases = $aliases + $convertedNames;
707 # Filter out aliases to namespaces that don't exist, e.g. from extensions
708 # that aren't loaded here but are included in the l10n cache.
709 # (array_intersect preserves keys from its first argument)
710 $this->namespaceAliases = array_intersect(
711 $this->namespaceAliases,
723 if ( is_null( $this->mNamespaceIds ) ) {
725 # Put namespace names and aliases into a hashtable.
726 # If this is too slow, then we should arrange it so that it is done
727 # before caching. The catch is that at pre-cache time, the above
728 # class-specific fixup hasn't been done.
729 $this->mNamespaceIds = [];
731 $this->mNamespaceIds[$this->
lc( $name )] = $index;
734 $this->mNamespaceIds[$this->
lc( $name )] = $index;
738 $this->mNamespaceIds[$this->
lc( $name )] = $index;
742 return $this->mNamespaceIds;
753 $lctext = $this->
lc( $text );
754 $ns = MWNamespace::getCanonicalIndex( $lctext );
755 if ( $ns !==
null ) {
759 return $ids[$lctext] ??
false;
770 $msg =
"variantname-$code";
771 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
774 $name = self::fetchLanguageName(
$code );
776 return $name; #
if it
's defined as a language name, show that
778 # otherwise, output the language code
786 public function getDatePreferences() {
787 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
793 function getDateFormats() {
794 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
800 public function getDefaultDateFormat() {
801 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
802 if ( $df === 'dmy or mdy
' ) {
803 global $wgAmericanDates;
804 return $wgAmericanDates ? 'mdy
' : 'dmy
';
813 public function getDatePreferenceMigrationMap() {
814 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
820 public function getExtraUserToggles() {
821 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
828 function getUserToggle( $tog ) {
829 return $this->getMessageFromDB( "tog-$tog" );
843 public static function fetchLanguageNames( $inLanguage = self::AS_AUTONYMS, $include = 'mw
' ) {
844 $cacheKey = $inLanguage === self::AS_AUTONYMS ? 'null' : $inLanguage;
845 $cacheKey .= ":$include";
846 if ( self::$languageNameCache === null ) {
847 self::$languageNameCache = new HashBagOStuff( [ 'maxKeys
' => 20 ] );
850 $ret = self::$languageNameCache->get( $cacheKey );
852 $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
853 self::$languageNameCache->set( $cacheKey, $ret );
868 private static function fetchLanguageNamesUncached(
869 $inLanguage = self::AS_AUTONYMS,
872 global $wgExtraLanguageNames, $wgUsePigLatinVariant;
874 // If passed an invalid language code to use, fallback to en
875 if ( $inLanguage !== self::AS_AUTONYMS && !self::isValidCode( $inLanguage ) ) {
882 # TODO: also include when $inLanguage is null, when this code is more efficient
883 Hooks::run( 'LanguageGetTranslatedLanguageNames
', [ &$names, $inLanguage ] );
886 $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
887 if ( $wgUsePigLatinVariant ) {
888 // Pig Latin (for variant development)
889 $mwNames['en-x-piglatin
'] = 'Igpay Atinlay
';
892 foreach ( $mwNames as $mwCode => $mwName ) {
893 # - Prefer own MediaWiki native name when not using the hook
894 # - For other names just add if not added through the hook
895 if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
896 $names[$mwCode] = $mwName;
900 if ( $include === self::ALL ) {
906 $coreCodes = array_keys( $mwNames );
907 foreach ( $coreCodes as $coreCode ) {
908 $returnMw[$coreCode] = $names[$coreCode];
911 if ( $include === self::SUPPORTED ) {
913 # We do this using a foreach over the codes instead of a directory
914 # loop so that messages files in extensions will work correctly.
915 foreach ( $returnMw as $code => $value ) {
916 if ( is_readable( self::getMessagesFileName( $code ) )
917 || is_readable( self::getJsonMessagesFileName( $code ) )
919 $namesMwFile[$code] = $names[$code];
923 ksort( $namesMwFile );
928 # 'mw
' option; default if it's not one of the other two
options (all/mwfile)
942 $inLanguage = self::AS_AUTONYMS,
946 $array = self::fetchLanguageNames( $inLanguage, $include );
947 return !array_key_exists(
$code, $array ) ?
'' : $array[
$code];
957 return $this->
msg( $msg )->text();
966 protected function msg( $msg ) {
967 return wfMessage( $msg )->inLanguage( $this );
982 $monthNames = [
'' ];
983 for ( $i = 1; $i < 13; $i++ ) {
1009 $monthNames = [
'' ];
1010 for ( $i = 1; $i < 13; $i++ ) {
1037 return $this->
getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1045 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1053 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1061 return $this->
getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1073 if ( !$dateTimeObj ) {
1074 $dateTimeObj = DateTime::createFromFormat(
1075 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1078 return $dateTimeObj->format(
$code );
1150 public function sprintfDate( $format, $ts, DateTimeZone $zone =
null, &$ttl =
'unused' ) {
1155 $dateTimeObj =
false;
1164 $usedSecond =
false;
1165 $usedMinute =
false;
1172 $usedISOYear =
false;
1173 $usedIsLeapYear =
false;
1175 $usedHebrewMonth =
false;
1176 $usedIranianMonth =
false;
1177 $usedHijriMonth =
false;
1178 $usedHebrewYear =
false;
1179 $usedIranianYear =
false;
1180 $usedHijriYear =
false;
1181 $usedTennoYear =
false;
1183 if ( strlen( $ts ) !== 14 ) {
1184 throw new MWException( __METHOD__ .
": The timestamp $ts should have 14 characters" );
1187 if ( !ctype_digit( $ts ) ) {
1188 throw new MWException( __METHOD__ .
": The timestamp $ts should be a number" );
1191 $formatLength = strlen( $format );
1192 for ( $p = 0; $p < $formatLength; $p++ ) {
1194 $code = $format[$p];
1195 if (
$code ==
'x' && $p < $formatLength - 1 ) {
1196 $code .= $format[++$p];
1199 if ( (
$code ===
'xi'
1205 && $p < $formatLength - 1 ) {
1206 $code .= $format[++$p];
1217 $rawToggle = !$rawToggle;
1230 $usedHebrewMonth =
true;
1232 $hebrew = self::tsToHebrew( $ts );
1238 $num = substr( $ts, 6, 2 );
1243 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1248 $num = intval( substr( $ts, 6, 2 ) );
1253 $iranian = self::tsToIranian( $ts );
1260 $hijri = self::tsToHijri( $ts );
1267 $hebrew = self::tsToHebrew( $ts );
1274 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1282 $usedIranianMonth =
true;
1284 $iranian = self::tsToIranian( $ts );
1289 $usedHijriMonth =
true;
1291 $hijri = self::tsToHijri( $ts );
1296 $usedHebrewMonth =
true;
1298 $hebrew = self::tsToHebrew( $ts );
1304 $num = substr( $ts, 4, 2 );
1312 $num = intval( substr( $ts, 4, 2 ) );
1315 $usedIranianMonth =
true;
1317 $iranian = self::tsToIranian( $ts );
1322 $usedHijriMonth =
true;
1324 $hijri = self::tsToHijri( $ts );
1329 $usedHebrewMonth =
true;
1331 $hebrew = self::tsToHebrew( $ts );
1336 $usedHebrewMonth =
true;
1338 $hebrew = self::tsToHebrew( $ts );
1344 $num = substr( $ts, 0, 4 );
1347 $usedIranianYear =
true;
1349 $iranian = self::tsToIranian( $ts );
1354 $usedHijriYear =
true;
1356 $hijri = self::tsToHijri( $ts );
1361 $usedHebrewYear =
true;
1363 $hebrew = self::tsToHebrew( $ts );
1370 $thai = self::tsToYear( $ts,
'thai' );
1377 $minguo = self::tsToYear( $ts,
'minguo' );
1382 $usedTennoYear =
true;
1384 $tenno = self::tsToYear( $ts,
'tenno' );
1390 $num = substr( $ts, 2, 2 );
1393 $usedIranianYear =
true;
1395 $iranian = self::tsToIranian( $ts );
1397 $num = substr( $iranian[0], -2 );
1400 $usedIranianYear =
true;
1402 $iranian = self::tsToIranian( $ts );
1404 $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1407 $usedIranianYear =
true;
1409 $iranian = self::tsToIranian( $ts );
1415 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' :
'pm';
1419 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' :
'PM';
1423 $h = substr( $ts, 8, 2 );
1424 $num = $h % 12 ? $h % 12 : 12;
1428 $num = intval( substr( $ts, 8, 2 ) );
1432 $h = substr( $ts, 8, 2 );
1433 $num = sprintf(
'%02d', $h % 12 ? $h % 12 : 12 );
1437 $num = substr( $ts, 8, 2 );
1441 $num = substr( $ts, 10, 2 );
1445 $num = substr( $ts, 12, 2 );
1455 $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1461 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1465 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1469 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1472 $usedIsLeapYear =
true;
1473 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1476 $usedISOYear =
true;
1477 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1484 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1487 # Backslash escaping
1488 if ( $p < $formatLength - 1 ) {
1489 $s .= $format[++$p];
1496 if ( $p < $formatLength - 1 ) {
1497 $endQuote = strpos( $format,
'"', $p + 1 );
1498 if ( $endQuote ===
false ) {
1499 # No terminating quote, assume literal "
1502 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1506 # Quote at end of string, assume literal "
1513 if ( $num !==
false ) {
1514 if ( $rawToggle || $raw ) {
1517 } elseif ( $roman ) {
1518 $s .= self::romanNumeral( $num );
1520 } elseif ( $hebrewNum ) {
1521 $s .= self::hebrewNumeral( $num );
1529 if ( $ttl ===
'unused' ) {
1531 } elseif ( $usedSecond ) {
1533 } elseif ( $usedMinute ) {
1534 $ttl = 60 - substr( $ts, 12, 2 );
1535 } elseif ( $usedHour ) {
1536 $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1537 } elseif ( $usedAMPM ) {
1538 $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 -
1539 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1543 $usedIranianMonth ||
1552 $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1553 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1556 $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1557 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1560 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400 +
1561 $timeRemainingInDay;
1562 } elseif ( $usedISOYear ) {
1565 $lastWeekOfISOYear = DateTime::createFromFormat(
1567 substr( $ts, 0, 4 ) .
'1228',
1568 $zone ?:
new DateTimeZone(
'UTC' )
1570 $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'W' );
1571 $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
1572 $timeRemainingInWeek =
1573 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400
1574 + $timeRemainingInDay;
1575 $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
1580 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
't' ) -
1581 substr( $ts, 6, 2 ) ) * 86400
1582 + $timeRemainingInDay;
1583 } elseif ( $usedYear ) {
1585 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1586 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1587 + $timeRemainingInDay;
1588 } elseif ( $usedIsLeapYear ) {
1589 $year = substr( $ts, 0, 4 );
1590 $timeRemainingInYear =
1591 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1592 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1593 + $timeRemainingInDay;
1595 if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1597 $nextCandidate = $year - $mod + 4;
1598 if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1599 $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1600 $timeRemainingInYear;
1602 $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1603 $timeRemainingInYear;
1607 $possibleTtls[] = $timeRemainingInYear;
1611 if ( $possibleTtls ) {
1612 $ttl = min( $possibleTtls );
1619 private static $GREG_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
1620 private static $IRANIAN_DAYS = [ 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ];
1635 $gy = substr( $ts, 0, 4 ) - 1600;
1636 $gm = substr( $ts, 4, 2 ) - 1;
1637 $gd = substr( $ts, 6, 2 ) - 1;
1639 # Days passed from the beginning (including leap years)
1641 + floor( ( $gy + 3 ) / 4 )
1642 - floor( ( $gy + 99 ) / 100 )
1643 + floor( ( $gy + 399 ) / 400 );
1646 for ( $i = 0; $i < $gm; $i++ ) {
1647 $gDayNo += self::$GREG_DAYS[$i];
1651 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1656 $gDayNo += (int)$gd;
1658 $jDayNo = $gDayNo - 79;
1660 $jNp = floor( $jDayNo / 12053 );
1663 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1666 if ( $jDayNo >= 366 ) {
1667 $jy += floor( ( $jDayNo - 1 ) / 365 );
1668 $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1673 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1674 $jDayNo -= self::$IRANIAN_DAYS[$i];
1680 return [ $jy, $jm, $jd, $jz ];
1695 $year = substr( $ts, 0, 4 );
1696 $month = substr( $ts, 4, 2 );
1697 $day = substr( $ts, 6, 2 );
1705 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1706 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1708 $zjd = (int)( ( 1461 * ( $zy + 4800 + (
int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1709 (
int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1710 (
int)( ( 3 * (int)( ( ( $zy + 4900 + (
int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1713 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (
int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1714 (
int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1717 $zl = $zjd - 1948440 + 10632;
1718 $zn = (int)( ( $zl - 1 ) / 10631 );
1719 $zl = $zl - 10631 * $zn + 354;
1720 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
1721 ( (int)( $zl / 5670 ) ) * ( (
int)( ( 43 * $zl ) / 15238 ) );
1722 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
1723 ( (int)( $zj / 16 ) ) * ( (
int)( ( 15238 * $zj ) / 43 ) ) + 29;
1724 $zm = (int)( ( 24 * $zl ) / 709 );
1725 $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1726 $zy = 30 * $zn + $zj - 30;
1728 return [ $zy, $zm, $zd ];
1748 $year = substr( $ts, 0, 4 );
1749 $month = substr( $ts, 4, 2 );
1750 $day = substr( $ts, 6, 2 );
1752 # Calculate Hebrew year
1753 $hebrewYear = $year + 3760;
1755 # Month number when September = 1, August = 12
1757 if ( $month > 12 ) {
1764 # Calculate day of year from 1 September
1766 for ( $i = 1; $i < $month; $i++ ) {
1770 # Check if the year is leap
1771 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1774 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1781 # Calculate the start of the Hebrew year
1782 $start = self::hebrewYearStart( $hebrewYear );
1784 # Calculate next year's start
1785 if ( $dayOfYear <= $start ) {
1786 # Day is before the start of the year - it is the previous year
1788 $nextStart = $start;
1792 # Add days since previous year's 1 September
1794 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1798 # Start of the new (previous) year
1799 $start = self::hebrewYearStart( $hebrewYear );
1802 $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1805 # Calculate Hebrew day of year
1806 $hebrewDayOfYear = $dayOfYear - $start;
1808 # Difference between year's days
1809 $diff = $nextStart - $start;
1810 # Add 12 (or 13 for leap years) days to ignore the difference between
1811 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1812 # difference is only about the year type
1813 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1819 # Check the year pattern, and is leap year
1820 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1821 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1822 # and non-leap years
1823 $yearPattern = $diff % 30;
1824 # Check if leap year
1825 $isLeap = $diff >= 30;
1827 # Calculate day in the month from number of day in the Hebrew year
1828 # Don't check Adar - if the day is not in Adar, we will stop before;
1829 # if it is in Adar, we will use it to check if it is Adar I or Adar II
1830 $hebrewDay = $hebrewDayOfYear;
1833 while ( $hebrewMonth <= 12 ) {
1834 # Calculate days in this month
1835 if ( $isLeap && $hebrewMonth == 6 ) {
1836 # Adar in a leap year
1838 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1840 if ( $hebrewDay <= $days ) {
1844 # Subtract the days of Adar I
1845 $hebrewDay -= $days;
1848 if ( $hebrewDay <= $days ) {
1854 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1855 # Cheshvan in a complete year (otherwise as the rule below)
1857 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1858 # Kislev in an incomplete year (otherwise as the rule below)
1861 # Odd months have 30 days, even have 29
1862 $days = 30 - ( $hebrewMonth - 1 ) % 2;
1864 if ( $hebrewDay <= $days ) {
1865 # In the current month
1868 # Subtract the days of the current month
1869 $hebrewDay -= $days;
1870 # Try in the next month
1875 return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1888 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1889 $b = intval( ( $year - 1 ) % 4 );
1890 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1894 $Mar = intval( $m );
1900 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1901 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1903 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1905 } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1909 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1926 $gy = substr( $ts, 0, 4 );
1927 $gm = substr( $ts, 4, 2 );
1928 $gd = substr( $ts, 6, 2 );
1930 if ( !strcmp( $cName,
'thai' ) ) {
1932 # Add 543 years to the Gregorian calendar
1933 # Months and days are identical
1934 $gy_offset = $gy + 543;
1935 # fix for dates between 1912 and 1941
1937 if ( $gy >= 1912 && $gy <= 1940 ) {
1941 $gm = ( $gm - 3 ) % 12;
1943 } elseif ( ( !strcmp( $cName,
'minguo' ) ) || !strcmp( $cName,
'juche' ) ) {
1945 # Deduct 1911 years from the Gregorian calendar
1946 # Months and days are identical
1947 $gy_offset = $gy - 1911;
1948 } elseif ( !strcmp( $cName,
'tenno' ) ) {
1949 # Nengō dates up to Meiji period
1950 # Deduct years from the Gregorian calendar
1951 # depending on the nengo periods
1952 # Months and days are identical
1954 || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1955 || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1958 $gy_gannen = $gy - 1868 + 1;
1959 $gy_offset = $gy_gannen;
1960 if ( $gy_gannen == 1 ) {
1963 $gy_offset =
'明治' . $gy_offset;
1965 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1966 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1967 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1968 ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1969 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1972 $gy_gannen = $gy - 1912 + 1;
1973 $gy_offset = $gy_gannen;
1974 if ( $gy_gannen == 1 ) {
1977 $gy_offset =
'大正' . $gy_offset;
1979 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1980 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1981 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1984 $gy_gannen = $gy - 1926 + 1;
1985 $gy_offset = $gy_gannen;
1986 if ( $gy_gannen == 1 ) {
1989 $gy_offset =
'昭和' . $gy_offset;
1991 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd >= 8 ) ) ||
1992 ( ( $gy > 1989 ) && ( $gy < 2019 ) ) ||
1993 ( ( $gy == 2019 ) && ( $gm < 5 ) )
1996 $gy_gannen = $gy - 1989 + 1;
1997 $gy_offset = $gy_gannen;
1998 if ( $gy_gannen == 1 ) {
2001 $gy_offset =
'平成' . $gy_offset;
2004 $gy_gannen = $gy - 2019 + 1;
2005 $gy_offset = $gy_gannen;
2006 if ( $gy_gannen == 1 ) {
2009 $gy_offset =
'令和' . $gy_offset;
2015 return [ $gy_offset, $gm, $gd ];
2032 if ( !preg_match( self::$strongDirRegex, $text,
$matches ) ) {
2050 [
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ],
2051 [
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ],
2052 [
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ],
2053 [
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
2054 'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' ]
2057 $num = intval( $num );
2058 if ( $num > 10000 || $num <= 0 ) {
2063 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2064 if ( $num >= $pow10 ) {
2065 $s .= $table[$i][(int)floor( $num / $pow10 )];
2067 $num = $num % $pow10;
2081 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ],
2082 [
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ],
2095 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ]
2098 $num = intval( $num );
2099 if ( $num > 9999 || $num <= 0 ) {
2104 if ( $num === 1000 ) {
2106 } elseif ( $num % 1000 === 0 ) {
2107 return $table[0][$num / 1000] .
"' אלפים";
2112 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2113 if ( $num >= $pow10 ) {
2114 if ( $num === 15 || $num === 16 ) {
2115 $letters[] = $table[0][9];
2116 $letters[] = $table[0][$num - 9];
2119 $letters = array_merge(
2121 (
array)$table[$i][intval( $num / $pow10 )]
2124 if ( $pow10 === 1000 ) {
2130 $num = $num % $pow10;
2133 $preTransformLength = count( $letters );
2134 if ( $preTransformLength === 1 ) {
2138 $lastIndex = $preTransformLength - 1;
2139 $letters[$lastIndex] = str_replace(
2140 [
'כ',
'מ',
'נ',
'פ',
'צ' ],
2141 [
'ך',
'ם',
'ן',
'ף',
'ץ' ],
2142 $letters[$lastIndex]
2148 if ( $letters[1] ===
"'" && $preTransformLength === 3 ) {
2151 array_splice( $letters, -1, 0,
'"' );
2155 return implode( $letters );
2169 if ( $tz ===
false ) {
2170 $tz = $wgUser->getOption(
'timecorrection' );
2173 $data = explode(
'|', $tz, 3 );
2175 if ( $data[0] ==
'ZoneInfo' ) {
2177 $userTZ =
new DateTimeZone( $data[2] );
2178 $date =
new DateTime( $ts,
new DateTimeZone(
'UTC' ) );
2179 $date->setTimezone( $userTZ );
2180 return $date->format(
'YmdHis' );
2181 }
catch ( Exception
$e ) {
2183 $data[0] =
'Offset';
2187 if ( $data[0] ==
'System' || $tz ==
'' ) {
2188 # Global offset in minutes.
2190 } elseif ( $data[0] ==
'Offset' ) {
2191 $minDiff = intval( $data[1] );
2193 $data = explode(
':', $tz );
2194 if ( count( $data ) == 2 ) {
2195 $data[0] = intval( $data[0] );
2196 $data[1] = intval( $data[1] );
2197 $minDiff = abs( $data[0] ) * 60 + $data[1];
2198 if ( $data[0] < 0 ) {
2199 $minDiff = -$minDiff;
2202 $minDiff = intval( $data[0] ) * 60;
2206 # No difference ? Return time unchanged
2207 if ( 0 == $minDiff ) {
2211 Wikimedia\suppressWarnings();
2212 # Generate an adjusted date; take advantage of the fact that mktime
2213 # will normalize out-of-range values so we don't have to split $minDiff
2214 # into hours and minutes.
2216 (
int)substr( $ts, 8, 2 ) ), # Hours
2217 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2218 (
int)substr( $ts, 12, 2 ), # Seconds
2219 (
int)substr( $ts, 4, 2 ), # Month
2220 (
int)substr( $ts, 6, 2 ), # Day
2221 (
int)substr( $ts, 0, 4 ) ); # Year
2223 $date =
date(
'YmdHis',
$t );
2224 Wikimedia\restoreWarnings();
2247 if ( is_bool( $usePrefs ) ) {
2249 $datePreference = $wgUser->getDatePreference();
2254 $datePreference = (
string)$usePrefs;
2258 if ( $datePreference ==
'' ) {
2262 return $datePreference;
2276 $wasDefault =
false;
2277 if ( $pref ==
'default' ) {
2282 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2283 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2285 if (
$type ===
'pretty' && $df ===
null ) {
2289 if ( !$wasDefault && $df ===
null ) {
2291 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2294 $this->dateFormatStrings[
$type][$pref] = $df;
2296 return $this->dateFormatStrings[
$type][$pref];
2309 public function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2312 $ts = $this->
userAdjust( $ts, $timecorrection );
2328 public function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2331 $ts = $this->
userAdjust( $ts, $timecorrection );
2348 public function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2351 $ts = $this->
userAdjust( $ts, $timecorrection );
2372 foreach ( $intervals as $intervalName => $intervalValue ) {
2375 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2376 $segments[] = $message->inLanguage( $this )->escaped();
2394 if ( empty( $chosenIntervals ) ) {
2395 $chosenIntervals = [
2407 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2408 $sortedNames = array_keys( $intervals );
2409 $smallestInterval = array_pop( $sortedNames );
2413 foreach ( $intervals as $name => $length ) {
2414 $value = floor( $seconds / $length );
2416 if (
$value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2417 $seconds -=
$value * $length;
2446 $options += [
'timecorrection' =>
true,
'format' =>
true ];
2447 if (
$options[
'timecorrection'] !==
false ) {
2448 if (
$options[
'timecorrection'] ===
true ) {
2449 $offset = $user->getOption(
'timecorrection' );
2451 $offset =
$options[
'timecorrection'];
2455 if (
$options[
'format'] ===
true ) {
2456 $format = $user->getDatePreference();
2551 if ( $relativeTo ===
null ) {
2554 if ( $user ===
null ) {
2555 $user = RequestContext::getMain()->getUser();
2559 $offsetThis =
$time->offsetForUser( $user );
2560 $offsetRel = $relativeTo->offsetForUser( $user );
2563 if ( Hooks::run(
'GetHumanTimestamp', [ &$ts,
$time, $relativeTo, $user, $this ] ) ) {
2568 $time->timestamp->sub( $offsetThis );
2569 $relativeTo->timestamp->sub( $offsetRel );
2588 $diff = $ts->diff( $relativeTo );
2589 $diffDay = (bool)( (
int)$ts->timestamp->format(
'w' ) -
2590 (int)$relativeTo->timestamp->format(
'w' ) );
2591 $days = $diff->days ?: (int)$diffDay;
2592 if ( $diff->invert || $days > 5
2593 && $ts->timestamp->format(
'Y' ) !== $relativeTo->timestamp->format(
'Y' )
2601 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2602 } elseif ( $days > 5 ) {
2604 $format = $this->
getDateFormatString(
'pretty', $user->getDatePreference() ?:
'default' );
2605 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2606 } elseif ( $days > 1 ) {
2609 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2613 ->inLanguage( $this )
2614 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2616 } elseif ( $days == 1 ) {
2620 ->inLanguage( $this )
2621 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2623 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2627 ->inLanguage( $this )
2628 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2633 } elseif ( $diff->h == 1 ) {
2635 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2636 } elseif ( $diff->i >= 1 ) {
2638 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2639 } elseif ( $diff->s >= 30 ) {
2641 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2655 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2662 return self::$dataCache->getItem( $this->mCode,
'messages' );
2673 # *input* string. We just ignore those too.
2676 Wikimedia\suppressWarnings();
2677 $text =
iconv( $in,
$out .
'//IGNORE', $string );
2678 Wikimedia\restoreWarnings();
2697 return mb_strtoupper(
$matches[0] );
2705 return mb_strtoupper(
$matches[0] );
2719 } elseif ( $o < 128 ) {
2723 return $this->
uc( $str,
true );
2735 public function uc( $str, $first =
false ) {
2738 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2743 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2754 return strval( $str );
2755 } elseif ( $o >= 128 ) {
2756 return $this->
lc( $str,
true );
2757 } elseif ( $o > 96 ) {
2760 $str[0] = strtolower( $str[0] );
2770 function lc( $str, $first =
false ) {
2773 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2775 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2778 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2787 return strlen( $str ) !== mb_strlen( $str );
2796 $str = $this->
lc( $str );
2799 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2802 return preg_replace_callback(
2804 [ $this,
'ucwordsCallbackMB' ],
2808 return ucwords( strtolower( $str ) );
2820 $str = $this->
lc( $str );
2823 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2826 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2827 "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2829 return preg_replace_callback(
2831 [ $this,
'ucwordbreaksCallbackMB' ],
2835 return preg_replace_callback(
2836 '/\b([\w\x80-\xff]+)\b/',
2837 [ $this,
'ucwordbreaksCallbackAscii' ],
2859 return $this->
uc(
$s );
2868 if ( is_array(
$s ) ) {
2869 throw new MWException(
'Given array to checkTitleEncoding.' );
2882 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2916 return self::convertDoubleWidth( $string );
2928 static $full =
null;
2929 static $half =
null;
2931 if ( $full ===
null ) {
2932 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2933 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2934 $full = str_split( $fullWidth, 3 );
2935 $half = str_split( $halfWidth );
2938 $string = str_replace( $full, $half, $string );
2948 $string = preg_replace( $pattern,
" $1 ", $string );
2949 $string = preg_replace(
'/ +/',
' ', $string );
2958 # some languages, e.g. Chinese, need to do a conversion
2959 # in order for search results to be displayed correctly
2972 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2973 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2979 if ( strlen(
$matches[1] ) != 3 ) {
2987 } elseif (
$code < 0xb098 ) {
2989 } elseif (
$code < 0xb2e4 ) {
2991 } elseif (
$code < 0xb77c ) {
2993 } elseif (
$code < 0xb9c8 ) {
2995 } elseif (
$code < 0xbc14 ) {
2997 } elseif (
$code < 0xc0ac ) {
2999 } elseif (
$code < 0xc544 ) {
3001 } elseif (
$code < 0xc790 ) {
3003 } elseif (
$code < 0xcc28 ) {
3005 } elseif (
$code < 0xce74 ) {
3007 } elseif (
$code < 0xd0c0 ) {
3009 } elseif (
$code < 0xd30c ) {
3011 } elseif (
$code < 0xd558 ) {
3062 $s = UtfNormal\Validator::cleanUp(
$s );
3086 if ( !isset( $this->transformData[$file] ) ) {
3088 $data = require
"$IP/languages/data/{$file}";
3091 return $this->transformData[$file]->replace( $string );
3100 return self::$dataCache->getItem( $this->mCode,
'rtl' );
3108 return $this->
isRTL() ?
'rtl' :
'ltr';
3120 return $this->
isRTL() ?
'right' :
'left';
3132 return $this->
isRTL() ?
'left' :
'right';
3148 return $this->
isRTL() ?
'‎' :
'‏';
3150 return $this->
isRTL() ?
'‏' :
'‎';
3164 $lrm =
"\u{200E}"; # LEFT-
TO-RIGHT MARK, commonly abbreviated LRM
3165 $rlm =
"\u{200F}"; # RIGHT-
TO-LEFT MARK, commonly abbreviated RLM
3167 return $this->
isRTL() ? $lrm : $rlm;
3169 return $this->
isRTL() ? $rlm : $lrm;
3176 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
3187 switch ( $direction ) {
3189 return $this->
isRTL() ?
'←' :
'→';
3191 return $this->
isRTL() ?
'→' :
'←';
3209 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
3217 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
3224 if ( $this->mMagicHookDone ) {
3227 $this->mMagicHookDone =
true;
3228 Hooks::run(
'LanguageGetMagic', [ &$this->mMagicExtensions, $this->
getCode() ],
'1.16' );
3238 if ( !$this->mMagicHookDone ) {
3242 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3243 $rawEntry = $this->mMagicExtensions[$mw->mId];
3245 $rawEntry = self::$dataCache->getSubitem(
3246 $this->mCode,
'magicWords', $mw->mId );
3249 if ( !is_array( $rawEntry ) ) {
3250 wfWarn(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3252 $mw->mCaseSensitive = $rawEntry[0];
3253 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3264 $fallbackChain = array_reverse( $fallbackChain );
3265 foreach ( $fallbackChain as
$code ) {
3266 if ( isset( $newWords[
$code] ) ) {
3267 $this->mMagicExtensions = $newWords[
$code] + $this->mMagicExtensions;
3279 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3281 $this->mExtendedSpecialPageAliases =
3282 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3283 Hooks::run(
'LanguageGetSpecialPageAliases',
3284 [ &$this->mExtendedSpecialPageAliases, $this->
getCode() ],
'1.16' );
3287 return $this->mExtendedSpecialPageAliases;
3297 return "<em>$text</em>";
3324 if ( !$nocommafy ) {
3325 $number = $this->
commafy( $number );
3328 $number = strtr( $number,
$s );
3335 $number = strtr( $number,
$s );
3339 return (
string)$number;
3351 return $this->
formatNum( $number,
true );
3362 $s = array_filter(
$s );
3363 $number = strtr( $number, array_flip(
$s ) );
3369 $s = array_filter(
$s );
3370 $number = strtr( $number, array_flip(
$s ) );
3373 $number = strtr( $number, [
',' =>
'' ] );
3386 if ( $number ===
null ) {
3397 $primaryGroupingSize = 3;
3400 if ( preg_match(
'/^\-?\d{1,' . $maximumLength .
'}(\.\d+)?$/', $number ) ) {
3404 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3408 if ( intval( $number ) < 0 ) {
3411 $number = substr( $number, 1 );
3416 preg_match(
"/\d+/", $number, $integerPart );
3417 preg_match(
"/\.\d*/", $number, $decimalPart );
3418 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3419 if ( $groupedNumber === $number ) {
3421 return $sign . $groupedNumber;
3423 $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3424 while ( $start > 0 ) {
3425 $match =
$matches[0][$numMatches - 1];
3426 $matchLen = strlen( $match );
3427 $start = $end - $matchLen;
3431 $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3433 if ( $numMatches > 1 ) {
3438 $groupedNumber =
"," . $groupedNumber;
3441 return $sign . $groupedNumber;
3449 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3456 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3463 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3470 return self::$dataCache->getItem( $this->mCode,
'minimumGroupingDigits' );
3482 $itemCount = count( $list );
3483 if ( $itemCount < 1 ) {
3486 $text = array_pop( $list );
3487 if ( $itemCount > 1 ) {
3488 $and = $this->
msg(
'and' )->escaped();
3489 $space = $this->
msg(
'word-separator' )->escaped();
3491 if ( $itemCount > 2 ) {
3492 $comma = $this->
msg(
'comma-separator' )->escaped();
3494 $text = implode( $comma, $list ) . $and . $space . $text;
3507 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3520 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3532 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3554 function truncate( $string, $length, $ellipsis =
'...', $adjustLength =
true ) {
3576 $string, $length, $ellipsis, $adjustLength,
'strlen',
'substr'
3604 $string, $length, $ellipsis, $adjustLength,
'mb_strlen',
'mb_substr'
3625 $string, $length, $ellipsis, $adjustLength, $measureLength, $getSubstring
3627 if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
3628 throw new InvalidArgumentException(
'Invalid callback provided' );
3631 # Check if there is no need to truncate
3632 if ( $measureLength( $string ) <= abs( $length ) ) {
3636 # Use the localized ellipsis character
3637 if ( $ellipsis ==
'...' ) {
3638 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3640 if ( $length == 0 ) {
3644 $stringOriginal = $string;
3645 # If ellipsis length is >= $length then we can't apply $adjustLength
3646 if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3647 $string = $ellipsis;
3648 # Otherwise, truncate and add ellipsis...
3650 $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3651 if ( $length > 0 ) {
3652 $length -= $ellipsisLength;
3653 $string = $getSubstring( $string, 0, $length );
3655 $string = rtrim( $string );
3656 $string = $string . $ellipsis;
3658 $length += $ellipsisLength;
3659 $string = $getSubstring( $string, $length );
3661 $string = ltrim( $string );
3662 $string = $ellipsis . $string;
3666 # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3667 # This check is *not* redundant if $adjustLength, due to the single case where
3668 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3669 if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3672 return $stringOriginal;
3684 if ( $string !=
'' ) {
3685 $char = ord( $string[strlen( $string ) - 1] );
3687 if ( $char >= 0xc0 ) {
3688 # We got the first byte only of a multibyte char; remove it.
3689 $string = substr( $string, 0, -1 );
3690 } elseif ( $char >= 0x80 &&
3692 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3693 '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3695 # We chopped in the middle of a character; remove it
3710 if ( $string !=
'' ) {
3711 $char = ord( $string[0] );
3712 if ( $char >= 0x80 && $char < 0xc0 ) {
3713 # We chopped in the middle of a character; remove the whole thing
3714 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3736 # Use the localized ellipsis character
3737 if ( $ellipsis ==
'...' ) {
3738 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3740 # Check if there is clearly no need to truncate
3741 if ( $length <= 0 ) {
3743 } elseif ( strlen( $text ) <= $length ) {
3748 $testingEllipsis =
false;
3756 $textLen = strlen( $text );
3757 $neLength = max( 0, $length - strlen( $ellipsis ) );
3758 for ( $pos = 0;
true; ++$pos ) {
3759 # Consider truncation once the display length has reached the maximim.
3760 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3761 # Check that we're not in the middle of a bracket/entity...
3762 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3763 if ( !$testingEllipsis ) {
3764 $testingEllipsis =
true;
3765 # Save where we are; we will truncate here unless there turn out to
3766 # be so few remaining characters that truncation is not necessary.
3767 if ( !$maybeState ) {
3768 $maybeState = [
$ret, $openTags ];
3770 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3771 # String in fact does need truncation, the truncation point was OK.
3772 list(
$ret, $openTags ) = $maybeState;
3778 if ( $pos >= $textLen ) {
3782 # Read the next char...
3784 $lastCh = $pos ? $text[$pos - 1] :
'';
3790 } elseif ( $ch ==
'>' ) {
3794 } elseif ( $bracketState == 1 ) {
3802 } elseif ( $bracketState == 2 ) {
3809 } elseif ( $bracketState == 0 ) {
3810 if ( $entityState ) {
3816 if ( $neLength == 0 && !$maybeState ) {
3819 $maybeState = [ substr(
$ret, 0, -1 ), $openTags ];
3826 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3828 $dispLen += $skipped;
3836 while ( count( $openTags ) > 0 ) {
3837 $ret .=
'</' . array_pop( $openTags ) .
'>';
3854 if ( $len ===
null ) {
3856 } elseif ( $len < 0 ) {
3860 if ( $start < strlen( $text ) ) {
3861 $skipCount = strcspn( $text, $search, $start, $len );
3862 $ret .= substr( $text, $start, $skipCount );
3877 $tag = ltrim( $tag );
3879 if ( $tagType == 0 && $lastCh !=
'/' ) {
3881 } elseif ( $tagType == 1 ) {
3882 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3883 array_pop( $openTags );
3912 if ( is_string( $forms ) ) {
3916 foreach ( array_values( $forms ) as $rule ) {
3919 if ( $form ===
'@metadata' ) {
3923 $replacement = $rule[1];
3925 $regex =
'/' . addcslashes( $form,
'/' ) .
'/u';
3926 $patternMatches = preg_match( $regex, $word );
3928 if ( $patternMatches ===
false ) {
3930 'An error occurred while processing grammar. ' .
3931 "Word: '$word'. Regex: /$form/."
3933 } elseif ( $patternMatches === 1 ) {
3934 $word = preg_replace( $regex, $replacement, $word );
3970 $languageCode = $this->
getCode();
3972 if ( self::$grammarTransformations ===
null ) {
3973 self::$grammarTransformations =
new MapCacheLRU( 10 );
3976 if ( self::$grammarTransformations->has( $languageCode ) ) {
3977 return self::$grammarTransformations->get( $languageCode );
3982 $grammarDataFile = __DIR__ .
"/data/grammarTransformations/$languageCode.json";
3983 if ( is_readable( $grammarDataFile ) ) {
3984 $data = FormatJson::decode(
3985 file_get_contents( $grammarDataFile ),
3989 if ( $data ===
null ) {
3990 throw new MWException(
"Invalid grammar data for \"$languageCode\"." );
3993 self::$grammarTransformations->set( $languageCode, $data );
4019 if ( !count( $forms ) ) {
4023 if ( $gender ===
'male' ) {
4026 if ( $gender ===
'female' ) {
4029 return $forms[2] ?? $forms[0];
4050 if ( is_string( $forms ) ) {
4053 if ( !count( $forms ) ) {
4058 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4059 return $forms[$pluralForm];
4078 foreach ( $forms as $index => $form ) {
4079 if ( preg_match(
'/\d+=/i', $form ) ) {
4080 $pos = strpos( $form,
'=' );
4081 if ( substr( $form, 0, $pos ) === (
string)$count ) {
4082 return substr( $form, $pos + 1 );
4084 unset( $forms[$index] );
4087 return array_values( $forms );
4099 while ( count( $forms ) < $count ) {
4100 $forms[] = $forms[count( $forms ) - 1];
4122 $dir = self::strongDirFromContent( $text );
4123 if ( $dir ===
'ltr' ) {
4125 return self::$lre . $text . self::$pdf;
4127 if ( $dir ===
'rtl' ) {
4129 return self::$rle . $text . self::$pdf;
4150 foreach ( $duration as $show =>
$value ) {
4151 if ( strcmp( $str,
$value ) == 0 ) {
4152 return htmlspecialchars( trim( $show ) );
4157 foreach ( $duration as $show =>
$value ) {
4159 return htmlspecialchars( trim( $show ) );
4165 $time = strtotime( $str, $now );
4166 if (
$time ===
false ) {
4168 } elseif (
$time !== strtotime( $str, $now + 1 ) ) {
4173 if (
$time === 0 ) {
4175 $time =
'19700101000000';
4212 return $this->mConverter;
4224 return $this->mConverter->autoConvert( $text, $variant );
4234 return $this->mConverter->autoConvertToAllVariants( $text );
4249 return $this->mConverter->convert( $text );
4259 return $this->mConverter->convertTitle( $title );
4271 return $this->mConverter->convertNamespace( $ns, $variant );
4294 return $variant && ( $variant === $this->mConverter->validateVariant( $variant ) );
4305 return htmlspecialchars( $this->convert( $text, $isTitle ) );
4313 return $this->mConverter->convertCategoryKey( $key );
4323 return $this->mConverter->getVariants();
4330 return $this->mConverter->getPreferredVariant();
4337 return $this->mConverter->getDefaultVariant();
4344 return $this->mConverter->getURLVariant();
4360 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
4370 return $this->mConverter->getExtraHashOptions();
4381 return $this->mConverter->getParsedTitle();
4391 $this->mConverter->updateConversionTable( $title );
4414 return $this->mConverter->markNoConversion( $text );
4427 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
4437 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
4448 if ( $this->mParentLanguage !==
false ) {
4449 return $this->mParentLanguage;
4453 if ( !in_array(
$code, LanguageConverter::$languagesWithVariants ) ) {
4454 $this->mParentLanguage =
null;
4458 if ( !
$lang->hasVariant( $this->getCode() ) ) {
4459 $this->mParentLanguage =
null;
4463 $this->mParentLanguage =
$lang;
4475 return $lang === $this ||
$lang->getCode() === $this->mCode;
4487 return $this->mCode;
4501 if ( is_null( $this->mHtmlCode ) ) {
4502 $this->mHtmlCode = LanguageCode::bcp47( $this->
getCode() );
4504 return $this->mHtmlCode;
4512 $this->mCode =
$code;
4514 $this->mHtmlCode =
null;
4515 $this->mParentLanguage =
false;
4527 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4528 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4529 if ( !count( $m ) ) {
4532 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4544 return 'Language' . str_replace(
'-',
'_',
ucfirst(
$code ) );
4557 if ( !self::isValidBuiltInCode(
$code ) ) {
4558 throw new MWException(
"Invalid language code \"$code\"" );
4561 return $prefix . str_replace(
'-',
'_',
ucfirst(
$code ) ) . $suffix;
4570 $file = self::getFileName(
"$IP/languages/messages/Messages",
$code,
'.php' );
4571 Hooks::run(
'Language::getMessagesFileName', [
$code, &$file ] );
4584 if ( !self::isValidBuiltInCode(
$code ) ) {
4585 throw new MWException(
"Invalid language code \"$code\"" );
4588 return "$IP/languages/i18n/$code.json";
4599 $fallbacks = self::getFallbacksFor(
$code );
4601 return $fallbacks[0];
4617 if (
$code ===
'en' || !self::isValidBuiltInCode(
$code ) ) {
4621 case self::MESSAGES_FALLBACKS:
4625 return self::getLocalisationCache()->getItem(
$code,
'fallbackSequence' ) ?: [
'en' ];
4626 case self::STRICT_FALLBACKS:
4630 return self::getLocalisationCache()->getItem(
$code,
'originalFallbackSequence' );
4632 throw new MWException(
"Invalid fallback mode \"$mode\"" );
4649 $cacheKey =
"{$code}-{$wgLanguageCode}";
4651 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4652 $fallbacks = self::getFallbacksFor(
$code );
4659 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4661 self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4663 return self::$fallbackLanguageCache[$cacheKey];
4676 return self::getLocalisationCache()->getItem(
$code,
'messages' );
4688 return self::getLocalisationCache()->getSubitem(
$code,
'messages', $key );
4700 return self::getLocalisationCache()->getSubitemList(
$code,
'messages' );
4708 if ( strpos( $talk,
'$1' ) ===
false ) {
4715 # Allow grammar transformations
4716 # Allowing full message-style parsing would make simple requests
4717 # such as action=raw much more expensive than they need to be.
4718 # This will hopefully cover most cases.
4719 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4720 [ $this,
'replaceGrammarInNamespace' ], $talk );
4721 return str_replace(
' ',
'_', $talk );
4742 public function formatExpiry( $expiry, $format =
true, $infinity =
'infinity' ) {
4744 if ( $dbInfinity ===
null ) {
4748 if ( $expiry ==
'' || $expiry ===
'infinity' || $expiry == $dbInfinity ) {
4749 return $format ===
true
4753 return $format ===
true
4773 if ( !is_array( $format ) ) {
4774 $format = [
'avoid' => $format ];
4776 if ( !isset( $format[
'avoid'] ) ) {
4777 $format[
'avoid'] =
false;
4779 if ( !isset( $format[
'noabbrevs'] ) ) {
4780 $format[
'noabbrevs'] =
false;
4783 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4785 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4787 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4789 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4791 if ( round( $seconds * 10 ) < 100 ) {
4792 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4793 $s = $secondsMsg->params(
$s )->text();
4794 } elseif ( round( $seconds ) < 60 ) {
4796 $s = $secondsMsg->params(
$s )->text();
4797 } elseif ( round( $seconds ) < 3600 ) {
4798 $minutes = floor( $seconds / 60 );
4799 $secondsPart = round( fmod( $seconds, 60 ) );
4800 if ( $secondsPart == 60 ) {
4804 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4806 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4807 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4808 $hours = floor( $seconds / 3600 );
4809 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4810 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4811 if ( $secondsPart == 60 ) {
4815 if ( $minutes == 60 ) {
4819 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4821 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4822 if ( !in_array( $format[
'avoid'], [
'avoidseconds',
'avoidminutes' ] ) ) {
4823 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4826 $days = floor( $seconds / 86400 );
4827 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4828 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4829 if ( $hours == 24 ) {
4833 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4835 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4836 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4837 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4838 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4839 if ( $minutes == 60 ) {
4843 if ( $hours == 24 ) {
4847 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4849 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4851 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4853 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4884 return str_replace(
'$1', $this->
formatNum( $size ),
4888 $sizes = [
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' ];
4891 $maxIndex = count( $sizes ) - 1;
4892 while ( $size >= $boundary && $index < $maxIndex ) {
4903 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4905 $size = round( $size, $round );
4907 return str_replace(
'$1', $this->
formatNum( $size ), $text );
4938 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) . $this->getDirMark();
4941 $this->
msg(
'word-separator' )->escaped() .
4942 $this->
msg(
'parentheses' )->rawParams( $details )->escaped();
4960 # Make 'previous' link
4961 $prev =
wfMessage(
'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4962 if ( $offset > 0 ) {
4963 $plink = $this->
numLink( $title, max( $offset - $limit, 0 ), $limit,
4964 $query, $prev,
'prevn-title',
'mw-prevlink' );
4966 $plink = htmlspecialchars( $prev );
4970 $next =
wfMessage(
'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4972 $nlink = htmlspecialchars( $next );
4974 $nlink = $this->
numLink( $title, $offset + $limit, $limit,
4975 $query, $next,
'nextn-title',
'mw-nextlink' );
4978 # Make links to set number of items per page
4980 foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
4981 $numLinks[] = $this->
numLink( $title, $offset, $num,
4985 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title( $title
4986 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
5004 $query = [
'limit' => $limit,
'offset' => $offset ] +
$query;
5005 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
5006 ->numParams( $limit )->text();
5008 return Html::element(
'a', [
'href' => $title->getLocalURL(
$query ),
5009 'title' => $tooltip,
'class' => $class ],
$link );
5018 return $this->mConverter->getConvRuleTitle();
5027 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
5028 $fallbacks = self::getFallbacksFor( $this->mCode );
5029 if ( !$pluralRules ) {
5030 foreach ( $fallbacks as $fallbackCode ) {
5031 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
5032 if ( $pluralRules ) {
5037 return $pluralRules;
5046 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
5047 $fallbacks = self::getFallbacksFor( $this->mCode );
5048 if ( !$pluralRules ) {
5049 foreach ( $fallbacks as $fallbackCode ) {
5050 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
5051 if ( $pluralRules ) {
5056 return $pluralRules;
5065 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
5066 $fallbacks = self::getFallbacksFor( $this->mCode );
5067 if ( !$pluralRuleTypes ) {
5068 foreach ( $fallbacks as $fallbackCode ) {
5069 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
5070 if ( $pluralRuleTypes ) {
5075 return $pluralRuleTypes;
5085 $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5100 if ( isset( $pluralRuleTypes[$index] ) ) {
5101 return $pluralRuleTypes[$index];
we sometimes make exceptions for this Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally NO WARRANTY BECAUSE THE PROGRAM IS LICENSED FREE OF THERE IS NO WARRANTY FOR THE TO THE EXTENT PERMITTED BY APPLICABLE LAW EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND OR OTHER PARTIES PROVIDE THE PROGRAM AS IS WITHOUT WARRANTY OF ANY EITHER EXPRESSED OR BUT NOT LIMITED TO
$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.
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
A fake language variant converter.
Simple store for keeping values in an associative array for the current process.
Base class for language conversion.
Internationalisation code.
const STRICT_FALLBACKS
Return a strict fallback chain in getFallbacksFor.
const MESSAGES_FALLBACKS
Return a fallback chain for messages in getFallbacksFor.
hasVariants()
Check if this is a language with variants.
initContLang()
Hook which will be called if this is the content language.
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
date( $ts, $adj=false, $format=true, $timecorrection=false)
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
static $strongDirRegex
Directionality test regex for embedBidi().
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
getPluralRuleTypes()
Get the plural rule types for the language.
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
convertCategoryKey( $key)
array null $namespaceNames
static getMessageKeysFor( $code)
Get all message keys for a given language.
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
static $mHebrewCalendarMonthGenMsgs
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
getHebrewCalendarMonthName( $key)
static getMessageFor( $key, $code)
Get a message for a given language.
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
truncateForVisual( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified number of characters, appending an optional string (e....
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
static clearCaches()
Intended for tests that may change configuration in a way that invalidates caches.
static array $fallbackLanguageCache
Cache for language fallbacks.
getMonthAbbreviation( $key)
getHebrewCalendarMonthNameGen( $key)
static $lre
Unicode directional formatting characters, for embedBidi()
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
truncateInternal( $string, $length, $ellipsis, $adjustLength, $measureLength, $getSubstring)
Internal method used for truncation.
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
hasVariant( $variant)
Strict check if the language has the specific variant.
static classFromCode( $code, $fallback=true)
transformUsingPairFile( $file, $string)
Transform a string using serialized data stored in the given file (which must be in the serialized su...
formatDuration( $seconds, array $chosenIntervals=[])
Takes a number of seconds and turns it into a text using values such as hours and minutes.
getHijriCalendarMonthName( $key)
const ALL
Return all known languages in fetchLanguageName(s).
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
ucwordbreaksCallbackAscii( $matches)
static LocalisationCache $dataCache
getPluralRuleType( $number)
Find the plural rule type appropriate for the given number For example, if the language is set to Ara...
normalize( $s)
Convert a UTF-8 string to normal form C.
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
getArrow( $direction='forwards')
An arrow, depending on the language direction.
equals(Language $lang)
Compare with an other language object.
ucwordbreaksCallbackMB( $matches)
convert( $text)
convert text to different variants of a language.
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
translateBlockExpiry( $str, User $user=null, $now=0)
truncateHtml( $text, $length, $ellipsis='...')
Truncate a string of valid HTML to a specified length in bytes, appending an optional string (e....
getGrammarTransformations()
Get the grammar transformations data for the language.
__destruct()
Reduce memory usage.
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
truncate_skip(&$ret, $text, $search, $start, $len=null)
truncateHtml() helper function like strcspn() but adds the skipped chars to $ret
getFormattedNamespaces()
A convenience function that returns getNamespaces() with spaces instead of underscores in values.
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
$transformData
ReplacementArray object caches.
userTimeAndDate( $ts, User $user, array $options=[])
Get the formatted date and time for the given timestamp and formatted for the given user.
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
commafy( $number)
Adds commas to a given number.
truncate_endBracket(&$tag, $tagType, $lastCh, &$openTags)
truncateHtml() helper function (a) push or pop $tag from $openTags as needed (b) clear $tag value
firstChar( $s)
Get the first character of a string.
doMagicHook()
Run the LanguageGetMagic hook once.
$mExtendedSpecialPageAliases
getGrammarForms()
Get the grammar forms for the content language.
static getFallbacksFor( $code, $mode=self::MESSAGES_FALLBACKS)
Get the ordered list of fallback languages.
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
static convertDoubleWidth( $string)
convert double-width roman characters to single-width.
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
getHtmlCode()
Get the code in BCP 47 format which we can use inside of html lang="" tags.
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
static array $durationIntervals
getNsIndex( $text)
Get a namespace key by value, case insensitive.
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
listToText(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
LanguageConverter $mConverter
caseFold( $s)
Return a case-folded representation of $s.
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
getCode()
Get the internal language code for this language object.
static $mIranianCalendarMonthMsgs
uc( $str, $first=false)
Convert a string to uppercase.
static $mHebrewCalendarMonthMsgs
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
truncateForDatabase( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
static newFromCode( $code, $fallback=false)
Create a language object for a given language code.
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
getMonthAbbreviationsArray()
static fetchLanguageName( $code, $inLanguage=self::AS_AUTONYMS, $include=self::ALL)
getMagic( $mw)
Fill a MagicWord object with data from here.
viewPrevNext(Title $title, $offset, $limit, array $query=[], $atend=false)
Generate (prev x| next x) (20|50|100...) type links for paging.
resetNamespaces()
Resets all of the namespace caches.
getMagicWords()
Get all magic words from cache.
emphasize( $text)
Italic is unsuitable for some languages.
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
convertForSearchResult( $termsArray)
sprintfDate( $format, $ts, DateTimeZone $zone=null, &$ttl='unused')
This is a workalike of PHP's date() function, but with better internationalisation,...
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
static HashBagOStuff null $languageNameCache
Cache for language names.
autoConvert( $text, $variant=false)
convert text to a variant
getIranianCalendarMonthName( $key)
getHumanTimestampInternal(MWTimestamp $ts, MWTimestamp $relativeTo, User $user)
Convert an MWTimestamp into a pretty human-readable timestamp using the given user preferences and re...
getHumanTimestamp(MWTimestamp $time, MWTimestamp $relativeTo=null, User $user=null)
Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
convertNamespace( $ns, $variant=null)
Convert a namespace index to a string in the preferred variant.
preConvertPlural($forms, $count)
Checks that convertPlural was given an array and pads it to requested amount of forms by copying the ...
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
static getFallbackFor( $code)
Get the first fallback for a given language.
getWeekdayAbbreviation( $key)
isRTL()
For right-to-left language support.
separatorTransformTable()
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
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
const AS_AUTONYMS
Return autonyms in fetchLanguageName(s).
iconv( $in, $out, $string)
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
fixVariableInNamespace( $talk)
static insertSpace( $string, $pattern)
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
const SUPPORTED
Return in fetchLanguageName(s) only the languages for which we have at least some localisation.
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
static getLocalisationCache()
Get the LocalisationCache instance.
static getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
msg( $msg)
Get message object in this language.
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
static getJsonMessagesFileName( $code)
ucwordbreaks( $str)
capitalize words at word breaks
ucfirst( $str)
Make a string's first character uppercase.
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
userTime( $ts, User $user, array $options=[])
Get the formatted time for the given timestamp and formatted for the given user.
getNsText( $index)
Get a namespace value by key.
hasWordBreaks()
Most writing systems use whitespace to break up words.
getConvRuleTitle()
Get the conversion rule title, if any.
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
static getMessagesFileName( $code)
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
time( $ts, $adj=false, $format=true, $timecorrection=false)
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
This method is deprecated since 1.31 and kept as alias for truncateForDatabase, which has replaced it...
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 romanNumeral( $num)
Roman number formatting up to 10000.
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
getCompiledPluralRules()
Get the compiled plural rules for the language.
Class for caching the contents of localisation files, Messages*.php and *.i18n.php.
Library for creating and parsing MW-style timestamps.
Handles a simple LRU key/value map with a maximum number of entries.
Wrapper around strtr() that holds replacements.
static getSuggestedDurations(Language $lang=null, $includeOther=true)
Get an array of suggested block durations from MediaWiki:Ipboptions.
static isUtf8( $value)
Test whether a string is valid UTF-8.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static getDefaultOption( $opt)
Get a given default option value.
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
see documentation in includes Linker php for Linker::makeImageLink & $time
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
namespace and then decline to actually register it & $namespaces
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that 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
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
if the prop value should be in the metadata multi language array format
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
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
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
if(!isset( $args[0])) $lang