29use CLDRPluralRuleParser\Evaluator;
65 'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
70 'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
74 'january',
'february',
'march',
'april',
'may_long',
'june',
75 'july',
'august',
'september',
'october',
'november',
79 'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
80 'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
84 'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
85 'sep',
'oct',
'nov',
'dec'
89 'iranian-calendar-m1',
'iranian-calendar-m2',
'iranian-calendar-m3',
90 'iranian-calendar-m4',
'iranian-calendar-m5',
'iranian-calendar-m6',
91 'iranian-calendar-m7',
'iranian-calendar-m8',
'iranian-calendar-m9',
92 'iranian-calendar-m10',
'iranian-calendar-m11',
'iranian-calendar-m12'
96 'hebrew-calendar-m1',
'hebrew-calendar-m2',
'hebrew-calendar-m3',
97 'hebrew-calendar-m4',
'hebrew-calendar-m5',
'hebrew-calendar-m6',
98 'hebrew-calendar-m7',
'hebrew-calendar-m8',
'hebrew-calendar-m9',
99 'hebrew-calendar-m10',
'hebrew-calendar-m11',
'hebrew-calendar-m12',
100 'hebrew-calendar-m6a',
'hebrew-calendar-m6b'
104 'hebrew-calendar-m1-gen',
'hebrew-calendar-m2-gen',
'hebrew-calendar-m3-gen',
105 'hebrew-calendar-m4-gen',
'hebrew-calendar-m5-gen',
'hebrew-calendar-m6-gen',
106 'hebrew-calendar-m7-gen',
'hebrew-calendar-m8-gen',
'hebrew-calendar-m9-gen',
107 'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
108 'hebrew-calendar-m6a-gen',
'hebrew-calendar-m6b-gen'
112 'hijri-calendar-m1',
'hijri-calendar-m2',
'hijri-calendar-m3',
113 'hijri-calendar-m4',
'hijri-calendar-m5',
'hijri-calendar-m6',
114 'hijri-calendar-m7',
'hijri-calendar-m8',
'hijri-calendar-m9',
115 'hijri-calendar-m10',
'hijri-calendar-m11',
'hijri-calendar-m12'
122 static public $durationIntervals = [
123 'millennia' => 31556952000,
124 'centuries' => 3155695200,
125 'decades' => 315569520,
140 static private $fallbackLanguageCache = [];
157 static private $lre =
"\xE2\x80\xAA";
158 static private $rle =
"\xE2\x80\xAB";
159 static private $pdf =
"\xE2\x80\xAC";
174 static private $strongDirRegex =
'/(?:([\x{41}-\x{5a}\x{61}-\x{7a}\x{aa}\x{b5}\x{ba}\x{c0}-\x{d6}\x{d8}-\x{f6}\x{f8}-\x{2b8}\x{2bb}-\x{2c1}\x{2d0}\x{2d1}\x{2e0}-\x{2e4}\x{2ee}\x{370}-\x{373}\x{376}\x{377}\x{37a}-\x{37d}\x{37f}\x{386}\x{388}-\x{38a}\x{38c}\x{38e}-\x{3a1}\x{3a3}-\x{3f5}\x{3f7}-\x{482}\x{48a}-\x{52f}\x{531}-\x{556}\x{559}-\x{55f}\x{561}-\x{587}\x{589}\x{903}-\x{939}\x{93b}\x{93d}-\x{940}\x{949}-\x{94c}\x{94e}-\x{950}\x{958}-\x{961}\x{964}-\x{980}\x{982}\x{983}\x{985}-\x{98c}\x{98f}\x{990}\x{993}-\x{9a8}\x{9aa}-\x{9b0}\x{9b2}\x{9b6}-\x{9b9}\x{9bd}-\x{9c0}\x{9c7}\x{9c8}\x{9cb}\x{9cc}\x{9ce}\x{9d7}\x{9dc}\x{9dd}\x{9df}-\x{9e1}\x{9e6}-\x{9f1}\x{9f4}-\x{9fa}\x{a03}\x{a05}-\x{a0a}\x{a0f}\x{a10}\x{a13}-\x{a28}\x{a2a}-\x{a30}\x{a32}\x{a33}\x{a35}\x{a36}\x{a38}\x{a39}\x{a3e}-\x{a40}\x{a59}-\x{a5c}\x{a5e}\x{a66}-\x{a6f}\x{a72}-\x{a74}\x{a83}\x{a85}-\x{a8d}\x{a8f}-\x{a91}\x{a93}-\x{aa8}\x{aaa}-\x{ab0}\x{ab2}\x{ab3}\x{ab5}-\x{ab9}\x{abd}-\x{ac0}\x{ac9}\x{acb}\x{acc}\x{ad0}\x{ae0}\x{ae1}\x{ae6}-\x{af0}\x{af9}\x{b02}\x{b03}\x{b05}-\x{b0c}\x{b0f}\x{b10}\x{b13}-\x{b28}\x{b2a}-\x{b30}\x{b32}\x{b33}\x{b35}-\x{b39}\x{b3d}\x{b3e}\x{b40}\x{b47}\x{b48}\x{b4b}\x{b4c}\x{b57}\x{b5c}\x{b5d}\x{b5f}-\x{b61}\x{b66}-\x{b77}\x{b83}\x{b85}-\x{b8a}\x{b8e}-\x{b90}\x{b92}-\x{b95}\x{b99}\x{b9a}\x{b9c}\x{b9e}\x{b9f}\x{ba3}\x{ba4}\x{ba8}-\x{baa}\x{bae}-\x{bb9}\x{bbe}\x{bbf}\x{bc1}\x{bc2}\x{bc6}-\x{bc8}\x{bca}-\x{bcc}\x{bd0}\x{bd7}\x{be6}-\x{bf2}\x{c01}-\x{c03}\x{c05}-\x{c0c}\x{c0e}-\x{c10}\x{c12}-\x{c28}\x{c2a}-\x{c39}\x{c3d}\x{c41}-\x{c44}\x{c58}-\x{c5a}\x{c60}\x{c61}\x{c66}-\x{c6f}\x{c7f}\x{c82}\x{c83}\x{c85}-\x{c8c}\x{c8e}-\x{c90}\x{c92}-\x{ca8}\x{caa}-\x{cb3}\x{cb5}-\x{cb9}\x{cbd}-\x{cc4}\x{cc6}-\x{cc8}\x{cca}\x{ccb}\x{cd5}\x{cd6}\x{cde}\x{ce0}\x{ce1}\x{ce6}-\x{cef}\x{cf1}\x{cf2}\x{d02}\x{d03}\x{d05}-\x{d0c}\x{d0e}-\x{d10}\x{d12}-\x{d3a}\x{d3d}-\x{d40}\x{d46}-\x{d48}\x{d4a}-\x{d4c}\x{d4e}\x{d57}\x{d5f}-\x{d61}\x{d66}-\x{d75}\x{d79}-\x{d7f}\x{d82}\x{d83}\x{d85}-\x{d96}\x{d9a}-\x{db1}\x{db3}-\x{dbb}\x{dbd}\x{dc0}-\x{dc6}\x{dcf}-\x{dd1}\x{dd8}-\x{ddf}\x{de6}-\x{def}\x{df2}-\x{df4}\x{e01}-\x{e30}\x{e32}\x{e33}\x{e40}-\x{e46}\x{e4f}-\x{e5b}\x{e81}\x{e82}\x{e84}\x{e87}\x{e88}\x{e8a}\x{e8d}\x{e94}-\x{e97}\x{e99}-\x{e9f}\x{ea1}-\x{ea3}\x{ea5}\x{ea7}\x{eaa}\x{eab}\x{ead}-\x{eb0}\x{eb2}\x{eb3}\x{ebd}\x{ec0}-\x{ec4}\x{ec6}\x{ed0}-\x{ed9}\x{edc}-\x{edf}\x{f00}-\x{f17}\x{f1a}-\x{f34}\x{f36}\x{f38}\x{f3e}-\x{f47}\x{f49}-\x{f6c}\x{f7f}\x{f85}\x{f88}-\x{f8c}\x{fbe}-\x{fc5}\x{fc7}-\x{fcc}\x{fce}-\x{fda}\x{1000}-\x{102c}\x{1031}\x{1038}\x{103b}\x{103c}\x{103f}-\x{1057}\x{105a}-\x{105d}\x{1061}-\x{1070}\x{1075}-\x{1081}\x{1083}\x{1084}\x{1087}-\x{108c}\x{108e}-\x{109c}\x{109e}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1360}-\x{137c}\x{1380}-\x{138f}\x{13a0}-\x{13f5}\x{13f8}-\x{13fd}\x{1401}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16f8}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1735}\x{1736}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17b6}\x{17be}-\x{17c5}\x{17c7}\x{17c8}\x{17d4}-\x{17da}\x{17dc}\x{17e0}-\x{17e9}\x{1810}-\x{1819}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191e}\x{1923}-\x{1926}\x{1929}-\x{192b}\x{1930}\x{1931}\x{1933}-\x{1938}\x{1946}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19b0}-\x{19c9}\x{19d0}-\x{19da}\x{1a00}-\x{1a16}\x{1a19}\x{1a1a}\x{1a1e}-\x{1a55}\x{1a57}\x{1a61}\x{1a63}\x{1a64}\x{1a6d}-\x{1a72}\x{1a80}-\x{1a89}\x{1a90}-\x{1a99}\x{1aa0}-\x{1aad}\x{1b04}-\x{1b33}\x{1b35}\x{1b3b}\x{1b3d}-\x{1b41}\x{1b43}-\x{1b4b}\x{1b50}-\x{1b6a}\x{1b74}-\x{1b7c}\x{1b82}-\x{1ba1}\x{1ba6}\x{1ba7}\x{1baa}\x{1bae}-\x{1be5}\x{1be7}\x{1bea}-\x{1bec}\x{1bee}\x{1bf2}\x{1bf3}\x{1bfc}-\x{1c2b}\x{1c34}\x{1c35}\x{1c3b}-\x{1c49}\x{1c4d}-\x{1c7f}\x{1cc0}-\x{1cc7}\x{1cd3}\x{1ce1}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf3}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{200e}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{214f}\x{2160}-\x{2188}\x{2336}-\x{237a}\x{2395}\x{249c}-\x{24e9}\x{26ac}\x{2800}-\x{28ff}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d70}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{302e}\x{302f}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{3190}-\x{31ba}\x{31f0}-\x{321c}\x{3220}-\x{324f}\x{3260}-\x{327b}\x{327f}-\x{32b0}\x{32c0}-\x{32cb}\x{32d0}-\x{32fe}\x{3300}-\x{3376}\x{337b}-\x{33dd}\x{33e0}-\x{33fe}\x{3400}-\x{4db5}\x{4e00}-\x{9fd5}\x{a000}-\x{a48c}\x{a4d0}-\x{a60c}\x{a610}-\x{a62b}\x{a640}-\x{a66e}\x{a680}-\x{a69d}\x{a6a0}-\x{a6ef}\x{a6f2}-\x{a6f7}\x{a722}-\x{a787}\x{a789}-\x{a7ad}\x{a7b0}-\x{a7b7}\x{a7f7}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a824}\x{a827}\x{a830}-\x{a837}\x{a840}-\x{a873}\x{a880}-\x{a8c3}\x{a8ce}-\x{a8d9}\x{a8f2}-\x{a8fd}\x{a900}-\x{a925}\x{a92e}-\x{a946}\x{a952}\x{a953}\x{a95f}-\x{a97c}\x{a983}-\x{a9b2}\x{a9b4}\x{a9b5}\x{a9ba}\x{a9bb}\x{a9bd}-\x{a9cd}\x{a9cf}-\x{a9d9}\x{a9de}-\x{a9e4}\x{a9e6}-\x{a9fe}\x{aa00}-\x{aa28}\x{aa2f}\x{aa30}\x{aa33}\x{aa34}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa4d}\x{aa50}-\x{aa59}\x{aa5c}-\x{aa7b}\x{aa7d}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aaeb}\x{aaee}-\x{aaf5}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{ab30}-\x{ab65}\x{ab70}-\x{abe4}\x{abe6}\x{abe7}\x{abe9}-\x{abec}\x{abf0}-\x{abf9}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{e000}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}\x{10000}-\x{1000b}\x{1000d}-\x{10026}\x{10028}-\x{1003a}\x{1003c}\x{1003d}\x{1003f}-\x{1004d}\x{10050}-\x{1005d}\x{10080}-\x{100fa}\x{10100}\x{10102}\x{10107}-\x{10133}\x{10137}-\x{1013f}\x{101d0}-\x{101fc}\x{10280}-\x{1029c}\x{102a0}-\x{102d0}\x{10300}-\x{10323}\x{10330}-\x{1034a}\x{10350}-\x{10375}\x{10380}-\x{1039d}\x{1039f}-\x{103c3}\x{103c8}-\x{103d5}\x{10400}-\x{1049d}\x{104a0}-\x{104a9}\x{10500}-\x{10527}\x{10530}-\x{10563}\x{1056f}\x{10600}-\x{10736}\x{10740}-\x{10755}\x{10760}-\x{10767}\x{11000}\x{11002}-\x{11037}\x{11047}-\x{1104d}\x{11066}-\x{1106f}\x{11082}-\x{110b2}\x{110b7}\x{110b8}\x{110bb}-\x{110c1}\x{110d0}-\x{110e8}\x{110f0}-\x{110f9}\x{11103}-\x{11126}\x{1112c}\x{11136}-\x{11143}\x{11150}-\x{11172}\x{11174}-\x{11176}\x{11182}-\x{111b5}\x{111bf}-\x{111c9}\x{111cd}\x{111d0}-\x{111df}\x{111e1}-\x{111f4}\x{11200}-\x{11211}\x{11213}-\x{1122e}\x{11232}\x{11233}\x{11235}\x{11238}-\x{1123d}\x{11280}-\x{11286}\x{11288}\x{1128a}-\x{1128d}\x{1128f}-\x{1129d}\x{1129f}-\x{112a9}\x{112b0}-\x{112de}\x{112e0}-\x{112e2}\x{112f0}-\x{112f9}\x{11302}\x{11303}\x{11305}-\x{1130c}\x{1130f}\x{11310}\x{11313}-\x{11328}\x{1132a}-\x{11330}\x{11332}\x{11333}\x{11335}-\x{11339}\x{1133d}-\x{1133f}\x{11341}-\x{11344}\x{11347}\x{11348}\x{1134b}-\x{1134d}\x{11350}\x{11357}\x{1135d}-\x{11363}\x{11480}-\x{114b2}\x{114b9}\x{114bb}-\x{114be}\x{114c1}\x{114c4}-\x{114c7}\x{114d0}-\x{114d9}\x{11580}-\x{115b1}\x{115b8}-\x{115bb}\x{115be}\x{115c1}-\x{115db}\x{11600}-\x{11632}\x{1163b}\x{1163c}\x{1163e}\x{11641}-\x{11644}\x{11650}-\x{11659}\x{11680}-\x{116aa}\x{116ac}\x{116ae}\x{116af}\x{116b6}\x{116c0}-\x{116c9}\x{11700}-\x{11719}\x{11720}\x{11721}\x{11726}\x{11730}-\x{1173f}\x{118a0}-\x{118f2}\x{118ff}\x{11ac0}-\x{11af8}\x{12000}-\x{12399}\x{12400}-\x{1246e}\x{12470}-\x{12474}\x{12480}-\x{12543}\x{13000}-\x{1342e}\x{14400}-\x{14646}\x{16800}-\x{16a38}\x{16a40}-\x{16a5e}\x{16a60}-\x{16a69}\x{16a6e}\x{16a6f}\x{16ad0}-\x{16aed}\x{16af5}\x{16b00}-\x{16b2f}\x{16b37}-\x{16b45}\x{16b50}-\x{16b59}\x{16b5b}-\x{16b61}\x{16b63}-\x{16b77}\x{16b7d}-\x{16b8f}\x{16f00}-\x{16f44}\x{16f50}-\x{16f7e}\x{16f93}-\x{16f9f}\x{1b000}\x{1b001}\x{1bc00}-\x{1bc6a}\x{1bc70}-\x{1bc7c}\x{1bc80}-\x{1bc88}\x{1bc90}-\x{1bc99}\x{1bc9c}\x{1bc9f}\x{1d000}-\x{1d0f5}\x{1d100}-\x{1d126}\x{1d129}-\x{1d166}\x{1d16a}-\x{1d172}\x{1d183}\x{1d184}\x{1d18c}-\x{1d1a9}\x{1d1ae}-\x{1d1e8}\x{1d360}-\x{1d371}\x{1d400}-\x{1d454}\x{1d456}-\x{1d49c}\x{1d49e}\x{1d49f}\x{1d4a2}\x{1d4a5}\x{1d4a6}\x{1d4a9}-\x{1d4ac}\x{1d4ae}-\x{1d4b9}\x{1d4bb}\x{1d4bd}-\x{1d4c3}\x{1d4c5}-\x{1d505}\x{1d507}-\x{1d50a}\x{1d50d}-\x{1d514}\x{1d516}-\x{1d51c}\x{1d51e}-\x{1d539}\x{1d53b}-\x{1d53e}\x{1d540}-\x{1d544}\x{1d546}\x{1d54a}-\x{1d550}\x{1d552}-\x{1d6a5}\x{1d6a8}-\x{1d6da}\x{1d6dc}-\x{1d714}\x{1d716}-\x{1d74e}\x{1d750}-\x{1d788}\x{1d78a}-\x{1d7c2}\x{1d7c4}-\x{1d7cb}\x{1d800}-\x{1d9ff}\x{1da37}-\x{1da3a}\x{1da6d}-\x{1da74}\x{1da76}-\x{1da83}\x{1da85}-\x{1da8b}\x{1f110}-\x{1f12e}\x{1f130}-\x{1f169}\x{1f170}-\x{1f19a}\x{1f1e6}-\x{1f202}\x{1f210}-\x{1f23a}\x{1f240}-\x{1f248}\x{1f250}\x{1f251}\x{20000}-\x{2a6d6}\x{2a700}-\x{2b734}\x{2b740}-\x{2b81d}\x{2b820}-\x{2cea1}\x{2f800}-\x{2fa1d}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}])|([\x{590}\x{5be}\x{5c0}\x{5c3}\x{5c6}\x{5c8}-\x{5ff}\x{7c0}-\x{7ea}\x{7f4}\x{7f5}\x{7fa}-\x{815}\x{81a}\x{824}\x{828}\x{82e}-\x{858}\x{85c}-\x{89f}\x{200f}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb4f}\x{10800}-\x{1091e}\x{10920}-\x{10a00}\x{10a04}\x{10a07}-\x{10a0b}\x{10a10}-\x{10a37}\x{10a3b}-\x{10a3e}\x{10a40}-\x{10ae4}\x{10ae7}-\x{10b38}\x{10b40}-\x{10e5f}\x{10e7f}-\x{10fff}\x{1e800}-\x{1e8cf}\x{1e8d7}-\x{1edff}\x{1ef00}-\x{1efff}\x{608}\x{60b}\x{60d}\x{61b}-\x{64a}\x{66d}-\x{66f}\x{671}-\x{6d5}\x{6e5}\x{6e6}\x{6ee}\x{6ef}\x{6fa}-\x{710}\x{712}-\x{72f}\x{74b}-\x{7a5}\x{7b1}-\x{7bf}\x{8a0}-\x{8e2}\x{fb50}-\x{fd3d}\x{fd40}-\x{fdcf}\x{fdf0}-\x{fdfc}\x{fdfe}\x{fdff}\x{fe70}-\x{fefe}\x{1ee00}-\x{1eeef}\x{1eef2}-\x{1eeff}]))/u';
191 $langObj = isset( self::$mLangObjCache[
$code] )
192 ? self::$mLangObjCache[
$code]
193 : self::newFromCode(
$code );
196 self::$mLangObjCache = array_merge( [
$code => $langObj ], self::$mLangObjCache );
211 if ( !self::isValidCode(
$code ) ) {
212 throw new MWException(
"Invalid language code \"$code\"" );
215 if ( !self::isValidBuiltInCode(
$code ) ) {
225 if ( class_exists( $class ) ) {
231 $fallbacks = self::getFallbacksFor(
$code );
232 foreach ( $fallbacks as $fallbackCode ) {
233 if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
234 throw new MWException(
"Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
237 $class = self::classFromCode( $fallbackCode );
238 if ( class_exists( $class ) ) {
245 throw new MWException(
"Invalid fallback sequence for language '$code'" );
257 if ( !self::isValidBuiltInCode(
$code ) ) {
261 if (
$code ===
'qqq' ) {
265 return is_readable( self::getMessagesFileName(
$code ) ) ||
266 is_readable( self::getJsonMessagesFileName(
$code ) );
287 $alphanum =
'[a-z0-9]';
288 $x =
'x'; #
private use singleton
289 $singleton =
'[a-wy-z]'; # other singleton
290 $s = $lenient ?
'[-_]' :
'-';
292 $language =
"$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
293 $script =
"$alpha{4}"; # ISO 15924
294 $region =
"(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2 or UN M.49
295 $variant =
"(?:$alphanum{5,8}|$digit$alphanum{3})";
296 $extension =
"$singleton(?:$s$alphanum{2,8})+";
297 $privateUse =
"$x(?:$s$alphanum{1,8})+";
299 # Define certain grandfathered codes, since otherwise the regex is pretty useless.
300 # Since these are limited, this is safe even later changes to the registry --
301 # the only oddity is that it might change the type of the tag, and thus
302 # the results from the capturing groups.
305 $grandfathered =
"en{$s}GB{$s}oed"
306 .
"|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
307 .
"|no{$s}(?:bok|nyn)"
308 .
"|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
309 .
"|zh{$s}min{$s}nan";
311 $variantList =
"$variant(?:$s$variant)*";
312 $extensionList =
"$extension(?:$s$extension)*";
314 $langtag =
"(?:($language)"
317 .
"(?:$s$variantList)?"
318 .
"(?:$s$extensionList)?"
319 .
"(?:$s$privateUse)?)";
321 # The final breakdown, with capturing groups for each of these components
322 # The variants, extensions, grandfathered, and private-use may have interior '-'
324 $root =
"^(?:$langtag|$privateUse|$grandfathered)$";
326 return (
bool)preg_match(
"/$root/", strtolower(
$code ) );
347 strcspn(
$code,
":/\\\000&<>'\"" ) === strlen(
$code )
364 if ( !is_string(
$code ) ) {
365 if ( is_object(
$code ) ) {
366 $addmsg =
" of class " . get_class(
$code );
371 throw new MWException( __METHOD__ .
" must be passed a string, $type given$addmsg" );
374 return (
bool)preg_match(
'/^[a-z0-9-]{2,}$/',
$code );
388 if ( !self::isValidBuiltInCode( $tag ) ) {
393 || self::fetchLanguageName( $tag, $tag ) !==
''
407 if ( is_null( self::$dataCache ) ) {
412 return self::$dataCache;
418 if ( static::class ===
'Language' ) {
421 $this->mCode = str_replace(
'_',
'-', strtolower( substr( static::class, 8 ) ) );
423 self::getLocalisationCache();
430 foreach ( $this as $name =>
$value ) {
431 unset( $this->$name );
447 return self::getFallbacksFor( $this->mCode );
455 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
465 if ( is_null( $this->namespaceNames ) ) {
468 $validNamespaces = MWNamespace::getCanonicalNamespaces();
471 self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
472 $this->namespaceNames += $validNamespaces;
483 # Sometimes a language will be localised but not actually exist on this wiki.
484 foreach ( $this->namespaceNames as $key => $text ) {
485 if ( !isset( $validNamespaces[$key] ) ) {
486 unset( $this->namespaceNames[$key] );
490 # The above mixing may leave namespaces out of canonical order.
491 # Re-order by namespace ID number...
492 ksort( $this->namespaceNames );
494 Hooks::run(
'LanguageGetNamespaces', [ &$this->namespaceNames ] );
506 $this->mNamespaceIds =
null;
513 $this->namespaceNames =
null;
514 $this->mNamespaceIds =
null;
515 $this->namespaceAliases =
null;
526 foreach ( $ns as $k => $v ) {
527 $ns[$k] = strtr( $v,
'_',
' ' );
545 return isset( $ns[$index] ) ? $ns[$index] :
false;
563 return strtr( $ns,
'_',
' ' );
578 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
580 return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->
getNsText( $index );
600 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
601 return count( $aliases ) > 0;
614 $lctext = $this->
lc( $text );
616 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
623 if ( is_null( $this->namespaceAliases ) ) {
624 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
628 foreach ( $aliases as $name => $index ) {
630 unset( $aliases[$name] );
632 $aliases[
$name] = $index;
639 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
640 foreach ( $genders as $index => $forms ) {
641 foreach ( $forms as $alias ) {
642 $aliases[$alias] = $index;
646 # Also add converted namespace names as aliases, to avoid confusion.
647 $convertedNames = [];
649 if ( $variant === $this->mCode ) {
653 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
657 $this->namespaceAliases = $aliases + $convertedNames;
667 if ( is_null( $this->mNamespaceIds ) ) {
669 # Put namespace names and aliases into a hashtable.
670 # If this is too slow, then we should arrange it so that it is done
671 # before caching. The catch is that at pre-cache time, the above
672 # class-specific fixup hasn't been done.
673 $this->mNamespaceIds = [];
675 $this->mNamespaceIds[$this->
lc( $name )] = $index;
678 $this->mNamespaceIds[$this->
lc( $name )] = $index;
682 $this->mNamespaceIds[$this->
lc( $name )] = $index;
686 return $this->mNamespaceIds;
697 $lctext = $this->
lc( $text );
698 $ns = MWNamespace::getCanonicalIndex( $lctext );
699 if ( $ns !==
null ) {
703 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
714 $msg =
"variantname-$code";
715 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
718 $name = self::fetchLanguageName(
$code );
720 return $name; #
if it
's defined as a language name, show that
722 # otherwise, output the language code
730 public function getDatePreferences() {
731 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
737 function getDateFormats() {
738 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
744 public function getDefaultDateFormat() {
745 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
746 if ( $df === 'dmy or mdy
' ) {
747 global $wgAmericanDates;
748 return $wgAmericanDates ? 'mdy
' : 'dmy
';
757 public function getDatePreferenceMigrationMap() {
758 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
765 function getImageFile( $image ) {
766 return self::$dataCache->getSubitem( $this->mCode, 'imageFiles
', $image );
773 public function getImageFiles() {
774 return self::$dataCache->getItem( $this->mCode, 'imageFiles
' );
780 public function getExtraUserToggles() {
781 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
788 function getUserToggle( $tog ) {
789 return $this->getMessageFromDB( "tog-$tog" );
803 public static function fetchLanguageNames( $inLanguage = null, $include = 'mw
' ) {
804 $cacheKey = $inLanguage === null ? 'null' : $inLanguage;
805 $cacheKey .= ":$include";
806 if ( self::$languageNameCache === null ) {
807 self::$languageNameCache = new HashBagOStuff( [ 'maxKeys
' => 20 ] );
810 $ret = self::$languageNameCache->get( $cacheKey );
812 $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
813 self::$languageNameCache->set( $cacheKey, $ret );
828 private static function fetchLanguageNamesUncached( $inLanguage = null, $include = 'mw
' ) {
829 global $wgExtraLanguageNames, $wgUsePigLatinVariant;
831 // If passed an invalid language code to use, fallback to en
832 if ( $inLanguage !== null && !self::isValidCode( $inLanguage ) ) {
839 # TODO: also include when $inLanguage is null, when this code is more efficient
840 Hooks::run( 'LanguageGetTranslatedLanguageNames
', [ &$names, $inLanguage ] );
843 $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
844 if ( $wgUsePigLatinVariant ) {
845 // Pig Latin (for variant development)
846 $mwNames['en-x-piglatin
'] = 'Igpay Atinlay
';
849 foreach ( $mwNames as $mwCode => $mwName ) {
850 # - Prefer own MediaWiki native name when not using the hook
851 # - For other names just add if not added through the hook
852 if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
853 $names[$mwCode] = $mwName;
857 if ( $include === 'all
' ) {
863 $coreCodes = array_keys( $mwNames );
864 foreach ( $coreCodes as $coreCode ) {
865 $returnMw[$coreCode] = $names[$coreCode];
868 if ( $include === 'mwfile
' ) {
870 # We do this using a foreach over the codes instead of a directory
871 # loop so that messages files in extensions will work correctly.
872 foreach ( $returnMw as $code => $value ) {
873 if ( is_readable( self::getMessagesFileName( $code ) )
874 || is_readable( self::getJsonMessagesFileName( $code ) )
876 $namesMwFile[$code] = $names[$code];
880 ksort( $namesMwFile );
885 # 'mw
' option; default if it's not one of the other two
options (all/mwfile)
898 $array = self::fetchLanguageNames( $inLanguage, $include );
899 return !array_key_exists(
$code, $array ) ?
'' : $array[
$code];
909 return $this->
msg( $msg )->text();
918 protected function msg( $msg ) {
919 return wfMessage( $msg )->inLanguage( $this );
934 $monthNames = [
'' ];
935 for ( $i = 1; $i < 13; $i++ ) {
961 $monthNames = [
'' ];
962 for ( $i = 1; $i < 13; $i++ ) {
989 return $this->
getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
997 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1005 return $this->
getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1013 return $this->
getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1025 if ( !$dateTimeObj ) {
1026 $dateTimeObj = DateTime::createFromFormat(
1027 'YmdHis', $ts, $zone ?:
new DateTimeZone(
'UTC' )
1030 return $dateTimeObj->format(
$code );
1102 public function sprintfDate( $format, $ts, DateTimeZone $zone =
null, &$ttl =
'unused' ) {
1107 $dateTimeObj =
false;
1116 $usedSecond =
false;
1117 $usedMinute =
false;
1124 $usedISOYear =
false;
1125 $usedIsLeapYear =
false;
1127 $usedHebrewMonth =
false;
1128 $usedIranianMonth =
false;
1129 $usedHijriMonth =
false;
1130 $usedHebrewYear =
false;
1131 $usedIranianYear =
false;
1132 $usedHijriYear =
false;
1133 $usedTennoYear =
false;
1135 if ( strlen( $ts ) !== 14 ) {
1136 throw new MWException( __METHOD__ .
": The timestamp $ts should have 14 characters" );
1139 if ( !ctype_digit( $ts ) ) {
1140 throw new MWException( __METHOD__ .
": The timestamp $ts should be a number" );
1143 $formatLength = strlen( $format );
1144 for ( $p = 0; $p < $formatLength; $p++ ) {
1146 $code = $format[$p];
1147 if (
$code ==
'x' && $p < $formatLength - 1 ) {
1148 $code .= $format[++$p];
1151 if ( (
$code ===
'xi'
1157 && $p < $formatLength - 1 ) {
1158 $code .= $format[++$p];
1169 $rawToggle = !$rawToggle;
1182 $usedHebrewMonth =
true;
1184 $hebrew = self::tsToHebrew( $ts );
1190 $num = substr( $ts, 6, 2 );
1195 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1200 $num = intval( substr( $ts, 6, 2 ) );
1205 $iranian = self::tsToIranian( $ts );
1212 $hijri = self::tsToHijri( $ts );
1219 $hebrew = self::tsToHebrew( $ts );
1226 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'w' ) + 1
1234 $usedIranianMonth =
true;
1236 $iranian = self::tsToIranian( $ts );
1241 $usedHijriMonth =
true;
1243 $hijri = self::tsToHijri( $ts );
1248 $usedHebrewMonth =
true;
1250 $hebrew = self::tsToHebrew( $ts );
1256 $num = substr( $ts, 4, 2 );
1264 $num = intval( substr( $ts, 4, 2 ) );
1267 $usedIranianMonth =
true;
1269 $iranian = self::tsToIranian( $ts );
1274 $usedHijriMonth =
true;
1276 $hijri = self::tsToHijri( $ts );
1281 $usedHebrewMonth =
true;
1283 $hebrew = self::tsToHebrew( $ts );
1288 $usedHebrewMonth =
true;
1290 $hebrew = self::tsToHebrew( $ts );
1296 $num = substr( $ts, 0, 4 );
1299 $usedIranianYear =
true;
1301 $iranian = self::tsToIranian( $ts );
1306 $usedHijriYear =
true;
1308 $hijri = self::tsToHijri( $ts );
1313 $usedHebrewYear =
true;
1315 $hebrew = self::tsToHebrew( $ts );
1322 $thai = self::tsToYear( $ts,
'thai' );
1329 $minguo = self::tsToYear( $ts,
'minguo' );
1334 $usedTennoYear =
true;
1336 $tenno = self::tsToYear( $ts,
'tenno' );
1342 $num = substr( $ts, 2, 2 );
1345 $usedIranianYear =
true;
1347 $iranian = self::tsToIranian( $ts );
1349 $num = substr( $iranian[0], -2 );
1352 $usedIranianYear =
true;
1354 $iranian = self::tsToIranian( $ts );
1356 $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1359 $usedIranianYear =
true;
1361 $iranian = self::tsToIranian( $ts );
1367 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'am' :
'pm';
1371 $s .= intval( substr( $ts, 8, 2 ) ) < 12 ?
'AM' :
'PM';
1375 $h = substr( $ts, 8, 2 );
1376 $num = $h % 12 ? $h % 12 : 12;
1380 $num = intval( substr( $ts, 8, 2 ) );
1384 $h = substr( $ts, 8, 2 );
1385 $num = sprintf(
'%02d', $h % 12 ? $h % 12 : 12 );
1389 $num = substr( $ts, 8, 2 );
1393 $num = substr( $ts, 10, 2 );
1397 $num = substr( $ts, 12, 2 );
1407 $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1413 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1417 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1421 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1424 $usedIsLeapYear =
true;
1425 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1428 $usedISOYear =
true;
1429 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1436 $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
$code );
1439 # Backslash escaping
1440 if ( $p < $formatLength - 1 ) {
1441 $s .= $format[++$p];
1448 if ( $p < $formatLength - 1 ) {
1449 $endQuote = strpos( $format,
'"', $p + 1 );
1450 if ( $endQuote ===
false ) {
1451 # No terminating quote, assume literal "
1454 $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1458 # Quote at end of string, assume literal "
1465 if ( $num !==
false ) {
1466 if ( $rawToggle || $raw ) {
1469 } elseif ( $roman ) {
1470 $s .= self::romanNumeral( $num );
1472 } elseif ( $hebrewNum ) {
1473 $s .= self::hebrewNumeral( $num );
1481 if ( $ttl ===
'unused' ) {
1483 } elseif ( $usedSecond ) {
1485 } elseif ( $usedMinute ) {
1486 $ttl = 60 - substr( $ts, 12, 2 );
1487 } elseif ( $usedHour ) {
1488 $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1489 } elseif ( $usedAMPM ) {
1490 $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 -
1491 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1495 $usedIranianMonth ||
1504 $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1505 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1508 $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1509 substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1512 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400 +
1513 $timeRemainingInDay;
1514 } elseif ( $usedISOYear ) {
1517 $lastWeekOfISOYear = DateTime::createFromFormat(
1519 substr( $ts, 0, 4 ) .
'1228',
1520 $zone ?:
new DateTimeZone(
'UTC' )
1522 $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'W' );
1523 $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
1524 $timeRemainingInWeek =
1525 ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'N' ) ) * 86400
1526 + $timeRemainingInDay;
1527 $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
1532 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
't' ) -
1533 substr( $ts, 6, 2 ) ) * 86400
1534 + $timeRemainingInDay;
1535 } elseif ( $usedYear ) {
1537 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1538 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1539 + $timeRemainingInDay;
1540 } elseif ( $usedIsLeapYear ) {
1541 $year = substr( $ts, 0, 4 );
1542 $timeRemainingInYear =
1543 ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'L' ) + 364 -
1544 self::dateTimeObjFormat( $dateTimeObj, $ts, $zone,
'z' ) ) * 86400
1545 + $timeRemainingInDay;
1547 if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1549 $nextCandidate = $year - $mod + 4;
1550 if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1551 $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1552 $timeRemainingInYear;
1554 $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1555 $timeRemainingInYear;
1559 $possibleTtls[] = $timeRemainingInYear;
1563 if ( $possibleTtls ) {
1564 $ttl = min( $possibleTtls );
1571 private static $GREG_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
1572 private static $IRANIAN_DAYS = [ 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ];
1587 $gy = substr( $ts, 0, 4 ) - 1600;
1588 $gm = substr( $ts, 4, 2 ) - 1;
1589 $gd = substr( $ts, 6, 2 ) - 1;
1591 # Days passed from the beginning (including leap years)
1593 + floor( ( $gy + 3 ) / 4 )
1594 - floor( ( $gy + 99 ) / 100 )
1595 + floor( ( $gy + 399 ) / 400 );
1598 for ( $i = 0; $i < $gm; $i++ ) {
1599 $gDayNo += self::$GREG_DAYS[$i];
1603 if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1608 $gDayNo += (int)$gd;
1610 $jDayNo = $gDayNo - 79;
1612 $jNp = floor( $jDayNo / 12053 );
1615 $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1618 if ( $jDayNo >= 366 ) {
1619 $jy += floor( ( $jDayNo - 1 ) / 365 );
1620 $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1625 for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1626 $jDayNo -= self::$IRANIAN_DAYS[$i];
1632 return [ $jy, $jm, $jd, $jz ];
1647 $year = substr( $ts, 0, 4 );
1648 $month = substr( $ts, 4, 2 );
1649 $day = substr( $ts, 6, 2 );
1657 ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1658 ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1660 $zjd = (int)( ( 1461 * ( $zy + 4800 + (
int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1661 (
int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1662 (
int)( ( 3 * (int)( ( ( $zy + 4900 + (
int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1665 $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (
int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1666 (
int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1669 $zl = $zjd - 1948440 + 10632;
1670 $zn = (int)( ( $zl - 1 ) / 10631 );
1671 $zl = $zl - 10631 * $zn + 354;
1672 $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
1673 ( (int)( $zl / 5670 ) ) * ( (
int)( ( 43 * $zl ) / 15238 ) );
1674 $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
1675 ( (int)( $zj / 16 ) ) * ( (
int)( ( 15238 * $zj ) / 43 ) ) + 29;
1676 $zm = (int)( ( 24 * $zl ) / 709 );
1677 $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1678 $zy = 30 * $zn + $zj - 30;
1680 return [ $zy, $zm, $zd ];
1700 $year = substr( $ts, 0, 4 );
1701 $month = substr( $ts, 4, 2 );
1702 $day = substr( $ts, 6, 2 );
1704 # Calculate Hebrew year
1705 $hebrewYear = $year + 3760;
1707 # Month number when September = 1, August = 12
1709 if ( $month > 12 ) {
1716 # Calculate day of year from 1 September
1718 for ( $i = 1; $i < $month; $i++ ) {
1722 # Check if the year is leap
1723 if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1726 } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1733 # Calculate the start of the Hebrew year
1734 $start = self::hebrewYearStart( $hebrewYear );
1736 # Calculate next year's start
1737 if ( $dayOfYear <= $start ) {
1738 # Day is before the start of the year - it is the previous year
1740 $nextStart = $start;
1744 # Add days since previous year's 1 September
1746 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1750 # Start of the new (previous) year
1751 $start = self::hebrewYearStart( $hebrewYear );
1754 $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1757 # Calculate Hebrew day of year
1758 $hebrewDayOfYear = $dayOfYear - $start;
1760 # Difference between year's days
1761 $diff = $nextStart - $start;
1762 # Add 12 (or 13 for leap years) days to ignore the difference between
1763 # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1764 # difference is only about the year type
1765 if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1771 # Check the year pattern, and is leap year
1772 # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1773 # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1774 # and non-leap years
1775 $yearPattern = $diff % 30;
1776 # Check if leap year
1777 $isLeap = $diff >= 30;
1779 # Calculate day in the month from number of day in the Hebrew year
1780 # Don't check Adar - if the day is not in Adar, we will stop before;
1781 # if it is in Adar, we will use it to check if it is Adar I or Adar II
1782 $hebrewDay = $hebrewDayOfYear;
1785 while ( $hebrewMonth <= 12 ) {
1786 # Calculate days in this month
1787 if ( $isLeap && $hebrewMonth == 6 ) {
1788 # Adar in a leap year
1790 # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1792 if ( $hebrewDay <= $days ) {
1796 # Subtract the days of Adar I
1797 $hebrewDay -= $days;
1800 if ( $hebrewDay <= $days ) {
1806 } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1807 # Cheshvan in a complete year (otherwise as the rule below)
1809 } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1810 # Kislev in an incomplete year (otherwise as the rule below)
1813 # Odd months have 30 days, even have 29
1814 $days = 30 - ( $hebrewMonth - 1 ) % 2;
1816 if ( $hebrewDay <= $days ) {
1817 # In the current month
1820 # Subtract the days of the current month
1821 $hebrewDay -= $days;
1822 # Try in the next month
1827 return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1840 $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1841 $b = intval( ( $year - 1 ) % 4 );
1842 $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1846 $Mar = intval( $m );
1852 $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1853 if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1855 } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1857 } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1861 $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1878 $gy = substr( $ts, 0, 4 );
1879 $gm = substr( $ts, 4, 2 );
1880 $gd = substr( $ts, 6, 2 );
1882 if ( !strcmp( $cName,
'thai' ) ) {
1884 # Add 543 years to the Gregorian calendar
1885 # Months and days are identical
1886 $gy_offset = $gy + 543;
1887 } elseif ( ( !strcmp( $cName,
'minguo' ) ) || !strcmp( $cName,
'juche' ) ) {
1889 # Deduct 1911 years from the Gregorian calendar
1890 # Months and days are identical
1891 $gy_offset = $gy - 1911;
1892 } elseif ( !strcmp( $cName,
'tenno' ) ) {
1893 # Nengō dates up to Meiji period
1894 # Deduct years from the Gregorian calendar
1895 # depending on the nengo periods
1896 # Months and days are identical
1898 || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1899 || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1902 $gy_gannen = $gy - 1868 + 1;
1903 $gy_offset = $gy_gannen;
1904 if ( $gy_gannen == 1 ) {
1907 $gy_offset =
'明治' . $gy_offset;
1909 ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1910 ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1911 ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1912 ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1913 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1916 $gy_gannen = $gy - 1912 + 1;
1917 $gy_offset = $gy_gannen;
1918 if ( $gy_gannen == 1 ) {
1921 $gy_offset =
'大正' . $gy_offset;
1923 ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1924 ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1925 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1928 $gy_gannen = $gy - 1926 + 1;
1929 $gy_offset = $gy_gannen;
1930 if ( $gy_gannen == 1 ) {
1933 $gy_offset =
'昭和' . $gy_offset;
1935 ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd >= 8 ) ) ||
1936 ( ( $gy > 1989 ) && ( $gy < 2019 ) ) ||
1937 ( ( $gy == 2019 ) && ( $gm < 5 ) )
1940 $gy_gannen = $gy - 1989 + 1;
1941 $gy_offset = $gy_gannen;
1942 if ( $gy_gannen == 1 ) {
1945 $gy_offset =
'平成' . $gy_offset;
1948 $gy_gannen = $gy - 2019 + 1;
1949 $gy_offset = $gy_gannen;
1950 if ( $gy_gannen == 1 ) {
1953 $gy_offset =
'令和' . $gy_offset;
1959 return [ $gy_offset, $gm, $gd ];
1976 if ( !preg_match( self::$strongDirRegex, $text,
$matches ) ) {
1994 [
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ],
1995 [
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ],
1996 [
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ],
1997 [
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
1998 'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' ]
2001 $num = intval( $num );
2002 if ( $num > 10000 || $num <= 0 ) {
2007 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2008 if ( $num >= $pow10 ) {
2009 $s .= $table[$i][(int)floor( $num / $pow10 )];
2011 $num = $num % $pow10;
2025 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ],
2026 [
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ],
2039 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ]
2042 $num = intval( $num );
2043 if ( $num > 9999 || $num <= 0 ) {
2048 if ( $num === 1000 ) {
2050 } elseif ( $num % 1000 === 0 ) {
2051 return $table[0][$num / 1000] .
"' אלפים";
2056 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2057 if ( $num >= $pow10 ) {
2058 if ( $num === 15 || $num === 16 ) {
2059 $letters[] = $table[0][9];
2060 $letters[] = $table[0][$num - 9];
2063 $letters = array_merge(
2065 (array)$table[$i][intval( $num / $pow10 )]
2068 if ( $pow10 === 1000 ) {
2074 $num = $num % $pow10;
2077 $preTransformLength = count( $letters );
2078 if ( $preTransformLength === 1 ) {
2082 $lastIndex = $preTransformLength - 1;
2083 $letters[$lastIndex] = str_replace(
2084 [
'כ',
'מ',
'נ',
'פ',
'צ' ],
2085 [
'ך',
'ם',
'ן',
'ף',
'ץ' ],
2086 $letters[$lastIndex]
2092 if ( $letters[1] ===
"'" && $preTransformLength === 3 ) {
2095 array_splice( $letters, -1, 0,
'"' );
2099 return implode( $letters );
2113 if ( $tz ===
false ) {
2114 $tz =
$wgUser->getOption(
'timecorrection' );
2117 $data = explode(
'|', $tz, 3 );
2119 if ( $data[0] ==
'ZoneInfo' ) {
2121 $userTZ =
new DateTimeZone( $data[2] );
2122 $date =
new DateTime( $ts,
new DateTimeZone(
'UTC' ) );
2123 $date->setTimezone( $userTZ );
2124 return $date->format(
'YmdHis' );
2125 }
catch ( Exception
$e ) {
2127 $data[0] =
'Offset';
2131 if ( $data[0] ==
'System' || $tz ==
'' ) {
2132 # Global offset in minutes.
2134 } elseif ( $data[0] ==
'Offset' ) {
2135 $minDiff = intval( $data[1] );
2137 $data = explode(
':', $tz );
2138 if ( count( $data ) == 2 ) {
2139 $data[0] = intval( $data[0] );
2140 $data[1] = intval( $data[1] );
2141 $minDiff = abs( $data[0] ) * 60 + $data[1];
2142 if ( $data[0] < 0 ) {
2143 $minDiff = -$minDiff;
2146 $minDiff = intval( $data[0] ) * 60;
2150 # No difference ? Return time unchanged
2151 if ( 0 == $minDiff ) {
2155 Wikimedia\suppressWarnings();
2156 # Generate an adjusted date; take advantage of the fact that mktime
2157 # will normalize out-of-range values so we don't have to split $minDiff
2158 # into hours and minutes.
2160 (
int)substr( $ts, 8, 2 ) ), # Hours
2161 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2162 (
int)substr( $ts, 12, 2 ), # Seconds
2163 (
int)substr( $ts, 4, 2 ), # Month
2164 (
int)substr( $ts, 6, 2 ), # Day
2165 (
int)substr( $ts, 0, 4 ) ); # Year
2167 $date =
date(
'YmdHis',
$t );
2168 Wikimedia\restoreWarnings();
2191 if ( is_bool( $usePrefs ) ) {
2193 $datePreference =
$wgUser->getDatePreference();
2198 $datePreference = (
string)$usePrefs;
2202 if ( $datePreference ==
'' ) {
2206 return $datePreference;
2220 $wasDefault =
false;
2221 if ( $pref ==
'default' ) {
2226 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2227 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2229 if (
$type ===
'pretty' && $df ===
null ) {
2233 if ( !$wasDefault && $df ===
null ) {
2235 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2238 $this->dateFormatStrings[
$type][$pref] = $df;
2240 return $this->dateFormatStrings[
$type][$pref];
2253 public function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2256 $ts = $this->
userAdjust( $ts, $timecorrection );
2272 public function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2275 $ts = $this->
userAdjust( $ts, $timecorrection );
2292 public function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2295 $ts = $this->
userAdjust( $ts, $timecorrection );
2316 foreach ( $intervals as $intervalName => $intervalValue ) {
2319 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2320 $segments[] = $message->inLanguage( $this )->escaped();
2338 if ( empty( $chosenIntervals ) ) {
2339 $chosenIntervals = [
2351 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2352 $sortedNames = array_keys( $intervals );
2353 $smallestInterval = array_pop( $sortedNames );
2357 foreach ( $intervals as $name => $length ) {
2358 $value = floor( $seconds / $length );
2360 if (
$value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2361 $seconds -=
$value * $length;
2390 $options += [
'timecorrection' =>
true,
'format' =>
true ];
2391 if (
$options[
'timecorrection'] !==
false ) {
2392 if (
$options[
'timecorrection'] ===
true ) {
2393 $offset = $user->getOption(
'timecorrection' );
2395 $offset =
$options[
'timecorrection'];
2399 if (
$options[
'format'] ===
true ) {
2400 $format = $user->getDatePreference();
2495 if ( $relativeTo ===
null ) {
2498 if ( $user ===
null ) {
2503 $offsetThis =
$time->offsetForUser( $user );
2504 $offsetRel = $relativeTo->offsetForUser( $user );
2507 if ( Hooks::run(
'GetHumanTimestamp', [ &$ts,
$time, $relativeTo, $user, $this ] ) ) {
2512 $time->timestamp->sub( $offsetThis );
2513 $relativeTo->timestamp->sub( $offsetRel );
2532 $diff = $ts->diff( $relativeTo );
2533 $diffDay = (bool)( (
int)$ts->timestamp->format(
'w' ) -
2534 (int)$relativeTo->timestamp->format(
'w' ) );
2535 $days = $diff->days ?: (int)$diffDay;
2536 if ( $diff->invert || $days > 5
2537 && $ts->timestamp->format(
'Y' ) !== $relativeTo->timestamp->format(
'Y' )
2545 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2546 } elseif ( $days > 5 ) {
2548 $format = $this->
getDateFormatString(
'pretty', $user->getDatePreference() ?:
'default' );
2549 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2550 } elseif ( $days > 1 ) {
2553 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2557 ->inLanguage( $this )
2558 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2560 } elseif ( $days == 1 ) {
2564 ->inLanguage( $this )
2565 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2567 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2571 ->inLanguage( $this )
2572 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2577 } elseif ( $diff->h == 1 ) {
2579 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2580 } elseif ( $diff->i >= 1 ) {
2582 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2583 } elseif ( $diff->s >= 30 ) {
2585 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2599 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2606 return self::$dataCache->getItem( $this->mCode,
'messages' );
2617 # *input* string. We just ignore those too.
2620 Wikimedia\suppressWarnings();
2621 $text =
iconv( $in,
$out .
'//IGNORE', $string );
2622 Wikimedia\restoreWarnings();
2641 return mb_strtoupper(
$matches[0] );
2649 return mb_strtoupper(
$matches[0] );
2663 } elseif ( $o < 128 ) {
2667 return $this->
uc( $str,
true );
2679 public function uc( $str, $first =
false ) {
2682 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2687 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2698 return strval( $str );
2699 } elseif ( $o >= 128 ) {
2700 return $this->
lc( $str,
true );
2701 } elseif ( $o > 96 ) {
2704 $str[0] = strtolower( $str[0] );
2714 function lc( $str, $first =
false ) {
2717 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2719 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2722 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2731 return strlen( $str ) !== mb_strlen( $str );
2740 $str = $this->
lc( $str );
2743 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2746 return preg_replace_callback(
2748 [ $this,
'ucwordsCallbackMB' ],
2752 return ucwords( strtolower( $str ) );
2764 $str = $this->
lc( $str );
2767 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2770 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2771 "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2773 return preg_replace_callback(
2775 [ $this,
'ucwordbreaksCallbackMB' ],
2779 return preg_replace_callback(
2780 '/\b([\w\x80-\xff]+)\b/',
2781 [ $this,
'ucwordbreaksCallbackAscii' ],
2803 return $this->
uc(
$s );
2812 if ( is_array(
$s ) ) {
2813 throw new MWException(
'Given array to checkTitleEncoding.' );
2826 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2860 return self::convertDoubleWidth( $string );
2872 static $full =
null;
2873 static $half =
null;
2875 if ( $full ===
null ) {
2876 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2877 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2878 $full = str_split( $fullWidth, 3 );
2879 $half = str_split( $halfWidth );
2882 $string = str_replace( $full, $half, $string );
2892 $string = preg_replace( $pattern,
" $1 ", $string );
2893 $string = preg_replace(
'/ +/',
' ', $string );
2902 # some languages, e.g. Chinese, need to do a conversion
2903 # in order for search results to be displayed correctly
2916 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2917 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2923 if ( strlen(
$matches[1] ) != 3 ) {
2931 } elseif (
$code < 0xb098 ) {
2932 return "\xe3\x84\xb1";
2933 } elseif (
$code < 0xb2e4 ) {
2934 return "\xe3\x84\xb4";
2935 } elseif (
$code < 0xb77c ) {
2936 return "\xe3\x84\xb7";
2937 } elseif (
$code < 0xb9c8 ) {
2938 return "\xe3\x84\xb9";
2939 } elseif (
$code < 0xbc14 ) {
2940 return "\xe3\x85\x81";
2941 } elseif (
$code < 0xc0ac ) {
2942 return "\xe3\x85\x82";
2943 } elseif (
$code < 0xc544 ) {
2944 return "\xe3\x85\x85";
2945 } elseif (
$code < 0xc790 ) {
2946 return "\xe3\x85\x87";
2947 } elseif (
$code < 0xcc28 ) {
2948 return "\xe3\x85\x88";
2949 } elseif (
$code < 0xce74 ) {
2950 return "\xe3\x85\x8a";
2951 } elseif (
$code < 0xd0c0 ) {
2952 return "\xe3\x85\x8b";
2953 } elseif (
$code < 0xd30c ) {
2954 return "\xe3\x85\x8c";
2955 } elseif (
$code < 0xd558 ) {
2956 return "\xe3\x85\x8d";
2958 return "\xe3\x85\x8e";
3003 $s = UtfNormal\Validator::cleanUp(
$s );
3027 if ( !isset( $this->transformData[$file] ) ) {
3029 if ( $data ===
false ) {
3030 throw new MWException( __METHOD__ .
": The transformation file $file is missing" );
3034 return $this->transformData[$file]->replace( $string );
3043 return self::$dataCache->getItem( $this->mCode,
'rtl' );
3051 return $this->
isRTL() ?
'rtl' :
'ltr';
3063 return $this->
isRTL() ?
'right' :
'left';
3075 return $this->
isRTL() ?
'left' :
'right';
3091 return $this->
isRTL() ?
'‎' :
'‏';
3093 return $this->
isRTL() ?
'‏' :
'‎';
3107 $lrm =
"\xE2\x80\x8E"; # LEFT-
TO-RIGHT MARK, commonly abbreviated LRM
3108 $rlm =
"\xE2\x80\x8F"; # RIGHT-
TO-LEFT MARK, commonly abbreviated RLM
3110 return $this->
isRTL() ? $lrm : $rlm;
3112 return $this->
isRTL() ? $rlm : $lrm;
3119 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
3130 switch ( $direction ) {
3132 return $this->
isRTL() ?
'←' :
'→';
3134 return $this->
isRTL() ?
'→' :
'←';
3152 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
3160 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
3167 if ( $this->mMagicHookDone ) {
3170 $this->mMagicHookDone =
true;
3171 Hooks::run(
'LanguageGetMagic', [ &$this->mMagicExtensions, $this->
getCode() ] );
3181 if ( !$this->mMagicHookDone ) {
3185 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3186 $rawEntry = $this->mMagicExtensions[$mw->mId];
3188 $rawEntry = self::$dataCache->getSubitem(
3189 $this->mCode,
'magicWords', $mw->mId );
3192 if ( !is_array( $rawEntry ) ) {
3193 wfWarn(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3195 $mw->mCaseSensitive = $rawEntry[0];
3196 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3207 $fallbackChain = array_reverse( $fallbackChain );
3208 foreach ( $fallbackChain as
$code ) {
3209 if ( isset( $newWords[
$code] ) ) {
3210 $this->mMagicExtensions = $newWords[
$code] + $this->mMagicExtensions;
3222 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3224 $this->mExtendedSpecialPageAliases =
3225 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3226 Hooks::run(
'LanguageGetSpecialPageAliases',
3227 [ &$this->mExtendedSpecialPageAliases, $this->
getCode() ] );
3230 return $this->mExtendedSpecialPageAliases;
3240 return "<em>$text</em>";
3267 if ( !$nocommafy ) {
3268 $number = $this->
commafy( $number );
3271 $number = strtr( $number,
$s );
3278 $number = strtr( $number,
$s );
3282 return (
string)$number;
3294 return $this->
formatNum( $number,
true );
3305 $s = array_filter(
$s );
3306 $number = strtr( $number, array_flip(
$s ) );
3312 $s = array_filter(
$s );
3313 $number = strtr( $number, array_flip(
$s ) );
3316 $number = strtr( $number, [
',' =>
'' ] );
3329 if ( $number ===
null ) {
3340 $primaryGroupingSize = 3;
3343 if ( preg_match(
'/^\-?\d{1,' . $maximumLength .
'}(\.\d+)?$/', $number ) ) {
3347 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3351 if ( intval( $number ) < 0 ) {
3354 $number = substr( $number, 1 );
3359 preg_match(
"/\d+/", $number, $integerPart );
3360 preg_match(
"/\.\d*/", $number, $decimalPart );
3361 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3362 if ( $groupedNumber === $number ) {
3364 return $sign . $groupedNumber;
3366 $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3367 while ( $start > 0 ) {
3368 $match =
$matches[0][$numMatches - 1];
3369 $matchLen = strlen( $match );
3370 $start = $end - $matchLen;
3374 $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3376 if ( $numMatches > 1 ) {
3381 $groupedNumber =
"," . $groupedNumber;
3384 return $sign . $groupedNumber;
3392 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3399 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3406 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3413 return self::$dataCache->getItem( $this->mCode,
'minimumGroupingDigits' );
3426 $m = count( $l ) - 1;
3431 $and = $this->
msg(
'and' )->escaped();
3432 $space = $this->
msg(
'word-separator' )->escaped();
3434 $comma = $this->
msg(
'comma-separator' )->escaped();
3438 for ( $i = $m - 1; $i >= 0; $i-- ) {
3439 if ( $i == $m - 1 ) {
3440 $s = $l[$i] . $and . $space .
$s;
3442 $s = $l[$i] . $comma .
$s;
3456 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3469 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3481 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3503 function truncate( $string, $length, $ellipsis =
'...', $adjustLength =
true ) {
3524 $string, $length, $ellipsis, $adjustLength,
'strlen',
'substr'
3552 $string, $length, $ellipsis, $adjustLength,
'mb_strlen',
'mb_substr'
3573 $string, $length, $ellipsis =
'...', $adjustLength =
true, $measureLength, $getSubstring
3575 if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
3576 throw new InvalidArgumentException(
'Invalid callback provided' );
3579 # Check if there is no need to truncate
3580 if ( $measureLength( $string ) <= abs( $length ) ) {
3584 # Use the localized ellipsis character
3585 if ( $ellipsis ==
'...' ) {
3586 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3588 if ( $length == 0 ) {
3592 $stringOriginal = $string;
3593 # If ellipsis length is >= $length then we can't apply $adjustLength
3594 if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3595 $string = $ellipsis;
3596 # Otherwise, truncate and add ellipsis...
3598 $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3599 if ( $length > 0 ) {
3600 $length -= $ellipsisLength;
3601 $string = $getSubstring( $string, 0, $length );
3603 $string = rtrim( $string );
3604 $string = $string . $ellipsis;
3606 $length += $ellipsisLength;
3607 $string = $getSubstring( $string, $length );
3609 $string = ltrim( $string );
3610 $string = $ellipsis . $string;
3614 # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3615 # This check is *not* redundant if $adjustLength, due to the single case where
3616 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3617 if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3620 return $stringOriginal;
3632 if ( $string !=
'' ) {
3633 $char = ord( $string[strlen( $string ) - 1] );
3635 if ( $char >= 0xc0 ) {
3636 # We got the first byte only of a multibyte char; remove it.
3637 $string = substr( $string, 0, -1 );
3638 } elseif ( $char >= 0x80 &&
3640 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3641 '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3643 # We chopped in the middle of a character; remove it
3658 if ( $string !=
'' ) {
3659 $char = ord( $string[0] );
3660 if ( $char >= 0x80 && $char < 0xc0 ) {
3661 # We chopped in the middle of a character; remove the whole thing
3662 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3684 # Use the localized ellipsis character
3685 if ( $ellipsis ==
'...' ) {
3686 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3688 # Check if there is clearly no need to truncate
3689 if ( $length <= 0 ) {
3691 } elseif ( strlen( $text ) <= $length ) {
3696 $testingEllipsis =
false;
3704 $textLen = strlen( $text );
3705 $neLength = max( 0, $length - strlen( $ellipsis ) );
3706 for ( $pos = 0;
true; ++$pos ) {
3707 # Consider truncation once the display length has reached the maximim.
3708 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3709 # Check that we're not in the middle of a bracket/entity...
3710 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3711 if ( !$testingEllipsis ) {
3712 $testingEllipsis =
true;
3713 # Save where we are; we will truncate here unless there turn out to
3714 # be so few remaining characters that truncation is not necessary.
3715 if ( !$maybeState ) {
3716 $maybeState = [
$ret, $openTags ];
3718 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3719 # String in fact does need truncation, the truncation point was OK.
3720 list(
$ret, $openTags ) = $maybeState;
3726 if ( $pos >= $textLen ) {
3730 # Read the next char...
3732 $lastCh = $pos ? $text[$pos - 1] :
'';
3738 } elseif ( $ch ==
'>' ) {
3742 } elseif ( $bracketState == 1 ) {
3750 } elseif ( $bracketState == 2 ) {
3757 } elseif ( $bracketState == 0 ) {
3758 if ( $entityState ) {
3764 if ( $neLength == 0 && !$maybeState ) {
3767 $maybeState = [ substr(
$ret, 0, -1 ), $openTags ];
3774 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3776 $dispLen += $skipped;
3784 while ( count( $openTags ) > 0 ) {
3785 $ret .=
'</' . array_pop( $openTags ) .
'>';
3802 if ( $len ===
null ) {
3804 } elseif ( $len < 0 ) {
3808 if ( $start < strlen( $text ) ) {
3809 $skipCount = strcspn( $text, $search, $start, $len );
3810 $ret .= substr( $text, $start, $skipCount );
3825 $tag = ltrim( $tag );
3827 if ( $tagType == 0 && $lastCh !=
'/' ) {
3829 } elseif ( $tagType == 1 ) {
3830 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3831 array_pop( $openTags );
3860 if ( is_string( $forms ) ) {
3864 foreach ( array_values( $forms ) as $rule ) {
3867 if ( $form ===
'@metadata' ) {
3871 $replacement = $rule[1];
3873 $regex =
'/' . addcslashes( $form,
'/' ) .
'/u';
3874 $patternMatches = preg_match( $regex, $word );
3876 if ( $patternMatches ===
false ) {
3878 'An error occurred while processing grammar. ' .
3879 "Word: '$word'. Regex: /$form/."
3881 } elseif ( $patternMatches === 1 ) {
3882 $word = preg_replace( $regex, $replacement, $word );
3918 $languageCode = $this->
getCode();
3920 if ( self::$grammarTransformations ===
null ) {
3921 self::$grammarTransformations =
new MapCacheLRU( 10 );
3924 if ( self::$grammarTransformations->has( $languageCode ) ) {
3925 return self::$grammarTransformations->get( $languageCode );
3930 $grammarDataFile = __DIR__ .
"/data/grammarTransformations/$languageCode.json";
3931 if ( is_readable( $grammarDataFile ) ) {
3932 $data = FormatJson::decode(
3933 file_get_contents( $grammarDataFile ),
3937 if ( $data ===
null ) {
3938 throw new MWException(
"Invalid grammar data for \"$languageCode\"." );
3941 self::$grammarTransformations->set( $languageCode, $data );
3967 if ( !count( $forms ) ) {
3971 if ( $gender ===
'male' ) {
3974 if ( $gender ===
'female' ) {
3977 return isset( $forms[2] ) ? $forms[2] : $forms[0];
3998 if ( is_string( $forms ) ) {
4001 if ( !count( $forms ) ) {
4006 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4007 return $forms[$pluralForm];
4026 foreach ( $forms as $index => $form ) {
4027 if ( preg_match(
'/\d+=/i', $form ) ) {
4028 $pos = strpos( $form,
'=' );
4029 if ( substr( $form, 0, $pos ) === (
string)$count ) {
4030 return substr( $form, $pos + 1 );
4032 unset( $forms[$index] );
4035 return array_values( $forms );
4047 while ( count( $forms ) < $count ) {
4048 $forms[] = $forms[count( $forms ) - 1];
4070 $dir = self::strongDirFromContent( $text );
4071 if ( $dir ===
'ltr' ) {
4073 return self::$lre . $text . self::$pdf;
4075 if ( $dir ===
'rtl' ) {
4077 return self::$rle . $text . self::$pdf;
4098 foreach ( $duration as $show =>
$value ) {
4099 if ( strcmp( $str,
$value ) == 0 ) {
4100 return trim( $show );
4105 foreach ( $duration as $show =>
$value ) {
4107 return trim( $show );
4113 $time = strtotime( $str, $now );
4114 if (
$time ===
false ) {
4116 } elseif (
$time !== strtotime( $str, $now + 1 ) ) {
4121 if (
$time === 0 ) {
4123 $time =
'19700101000000';
4160 return $this->mConverter;
4172 return $this->mConverter->autoConvert( $text, $variant );
4182 return $this->mConverter->autoConvertToAllVariants( $text );
4192 return $this->mConverter->convert( $text );
4202 return $this->mConverter->convertTitle( $title );
4214 return $this->mConverter->convertNamespace( $ns, $variant );
4234 return (
bool)$this->mConverter->validateVariant( $variant );
4245 return htmlspecialchars( $this->convert( $text, $isTitle ) );
4253 return $this->mConverter->convertCategoryKey( $key );
4263 return $this->mConverter->getVariants();
4270 return $this->mConverter->getPreferredVariant();
4277 return $this->mConverter->getDefaultVariant();
4284 return $this->mConverter->getURLVariant();
4300 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
4310 return $this->mConverter->getExtraHashOptions();
4321 return $this->mConverter->getParsedTitle();
4331 $this->mConverter->updateConversionTable( $title );
4349 return $this->mConverter->markNoConversion( $text );
4362 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
4372 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
4383 if ( $this->mParentLanguage !==
false ) {
4384 return $this->mParentLanguage;
4388 if ( !in_array(
$code, LanguageConverter::$languagesWithVariants ) ) {
4389 $this->mParentLanguage =
null;
4393 if ( !
$lang->hasVariant( $this->getCode() ) ) {
4394 $this->mParentLanguage =
null;
4398 $this->mParentLanguage =
$lang;
4410 return $lang->getCode() === $this->mCode;
4422 return $this->mCode;
4436 if ( is_null( $this->mHtmlCode ) ) {
4437 $this->mHtmlCode = LanguageCode::bcp47( $this->
getCode() );
4439 return $this->mHtmlCode;
4446 $this->mCode =
$code;
4448 $this->mHtmlCode =
null;
4449 $this->mParentLanguage =
false;
4461 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4462 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4463 if ( !count( $m ) ) {
4466 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4478 return 'Language' . str_replace(
'-',
'_',
ucfirst(
$code ) );
4491 if ( !self::isValidBuiltInCode(
$code ) ) {
4492 throw new MWException(
"Invalid language code \"$code\"" );
4495 return $prefix . str_replace(
'-',
'_',
ucfirst(
$code ) ) . $suffix;
4504 $file = self::getFileName(
"$IP/languages/messages/Messages",
$code,
'.php' );
4505 Hooks::run(
'Language::getMessagesFileName', [
$code, &$file ] );
4518 if ( !self::isValidBuiltInCode(
$code ) ) {
4519 throw new MWException(
"Invalid language code \"$code\"" );
4522 return "$IP/languages/i18n/$code.json";
4533 $fallbacks = self::getFallbacksFor(
$code );
4535 return $fallbacks[0];
4548 if (
$code ===
'en' || !self::isValidBuiltInCode(
$code ) ) {
4553 return self::getLocalisationCache()->getItem(
$code,
'fallbackSequence' ) ?: [
'en' ];
4569 $cacheKey =
"{$code}-{$wgLanguageCode}";
4571 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4572 $fallbacks = self::getFallbacksFor(
$code );
4579 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4581 self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4583 return self::$fallbackLanguageCache[$cacheKey];
4596 return self::getLocalisationCache()->getItem(
$code,
'messages' );
4608 return self::getLocalisationCache()->getSubitem(
$code,
'messages', $key );
4620 return self::getLocalisationCache()->getSubitemList(
$code,
'messages' );
4628 if ( strpos( $talk,
'$1' ) ===
false ) {
4635 # Allow grammar transformations
4636 # Allowing full message-style parsing would make simple requests
4637 # such as action=raw much more expensive than they need to be.
4638 # This will hopefully cover most cases.
4639 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4640 [ $this,
'replaceGrammarInNamespace' ], $talk );
4641 return str_replace(
' ',
'_', $talk );
4662 public function formatExpiry( $expiry, $format =
true, $infinity =
'infinity' ) {
4664 if ( $dbInfinity ===
null ) {
4668 if ( $expiry ==
'' || $expiry ===
'infinity' || $expiry == $dbInfinity ) {
4669 return $format ===
true
4673 return $format ===
true
4693 if ( !is_array( $format ) ) {
4694 $format = [
'avoid' => $format ];
4696 if ( !isset( $format[
'avoid'] ) ) {
4697 $format[
'avoid'] =
false;
4699 if ( !isset( $format[
'noabbrevs'] ) ) {
4700 $format[
'noabbrevs'] =
false;
4703 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4705 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4707 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4709 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4711 if ( round( $seconds * 10 ) < 100 ) {
4712 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4713 $s = $secondsMsg->params(
$s )->text();
4714 } elseif ( round( $seconds ) < 60 ) {
4716 $s = $secondsMsg->params(
$s )->text();
4717 } elseif ( round( $seconds ) < 3600 ) {
4718 $minutes = floor( $seconds / 60 );
4719 $secondsPart = round( fmod( $seconds, 60 ) );
4720 if ( $secondsPart == 60 ) {
4724 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4726 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4727 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4728 $hours = floor( $seconds / 3600 );
4729 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4730 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4731 if ( $secondsPart == 60 ) {
4735 if ( $minutes == 60 ) {
4739 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4741 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4742 if ( !in_array( $format[
'avoid'], [
'avoidseconds',
'avoidminutes' ] ) ) {
4743 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4746 $days = floor( $seconds / 86400 );
4747 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4748 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4749 if ( $hours == 24 ) {
4753 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4755 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4756 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4757 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4758 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4759 if ( $minutes == 60 ) {
4763 if ( $hours == 24 ) {
4767 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4769 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4771 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4773 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4804 return str_replace(
'$1', $this->
formatNum( $size ),
4808 $sizes = [
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' ];
4811 $maxIndex = count( $sizes ) - 1;
4812 while ( $size >= $boundary && $index < $maxIndex ) {
4823 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4825 $size = round( $size, $round );
4827 return str_replace(
'$1', $this->
formatNum( $size ), $text );
4858 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) . $this->getDirMark();
4861 $this->
msg(
'word-separator' )->escaped() .
4862 $this->
msg(
'parentheses' )->rawParams( $details )->escaped();
4876 array
$query = [], $atend =
false
4880 # Make 'previous' link
4881 $prev =
wfMessage(
'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4882 if ( $offset > 0 ) {
4883 $plink = $this->
numLink( $title, max( $offset - $limit, 0 ), $limit,
4884 $query, $prev,
'prevn-title',
'mw-prevlink' );
4886 $plink = htmlspecialchars( $prev );
4890 $next =
wfMessage(
'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4892 $nlink = htmlspecialchars( $next );
4894 $nlink = $this->
numLink( $title, $offset + $limit, $limit,
4895 $query, $next,
'nextn-title',
'mw-nextlink' );
4898 # Make links to set number of items per page
4900 foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
4901 $numLinks[] = $this->
numLink( $title, $offset, $num,
4905 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title( $title
4906 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
4924 $query = [
'limit' => $limit,
'offset' => $offset ] +
$query;
4925 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
4926 ->numParams( $limit )->text();
4928 return Html::element(
'a', [
'href' => $title->getLocalURL(
$query ),
4929 'title' => $tooltip,
'class' => $class ],
$link );
4938 return $this->mConverter->getConvRuleTitle();
4947 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
4948 $fallbacks = self::getFallbacksFor( $this->mCode );
4949 if ( !$pluralRules ) {
4950 foreach ( $fallbacks as $fallbackCode ) {
4951 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
4952 if ( $pluralRules ) {
4957 return $pluralRules;
4966 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
4967 $fallbacks = self::getFallbacksFor( $this->mCode );
4968 if ( !$pluralRules ) {
4969 foreach ( $fallbacks as $fallbackCode ) {
4970 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
4971 if ( $pluralRules ) {
4976 return $pluralRules;
4985 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
4986 $fallbacks = self::getFallbacksFor( $this->mCode );
4987 if ( !$pluralRuleTypes ) {
4988 foreach ( $fallbacks as $fallbackCode ) {
4989 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
4990 if ( $pluralRuleTypes ) {
4995 return $pluralRuleTypes;
5005 $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5020 if ( isset( $pluralRuleTypes[$index] ) ) {
5021 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.
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
A fake language variant converter.
Simple store for keeping values in an associative array for the current process.
Base class for language conversion.
Internationalisation code.
hasVariants()
Check if this is a language with variants.
initContLang()
Hook which will be called if this is the content language.
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
date( $ts, $adj=false, $format=true, $timecorrection=false)
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
static $strongDirRegex
Directionality test regex for embedBidi().
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
getPluralRuleTypes()
Get the plural rule types for the language.
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
convertCategoryKey( $key)
array null $namespaceNames
static getMessageKeysFor( $code)
Get all message keys for a given language.
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
static $mHebrewCalendarMonthGenMsgs
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
getHebrewCalendarMonthName( $key)
static getMessageFor( $key, $code)
Get a message for a given language.
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
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 getFallbacksFor( $code)
Get the ordered list of fallback languages.
static array $fallbackLanguageCache
Cache for language fallbacks.
getMonthAbbreviation( $key)
getHebrewCalendarMonthNameGen( $key)
static $lre
Unicode directional formatting characters, for embedBidi()
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
hasVariant( $variant)
Check if the language has the specific variant.
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)
static fetchLanguageName( $code, $inLanguage=null, $include='all')
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
static getFileName( $prefix='Language', $code, $suffix='.php')
Get the name of a file for a certain language code.
ucwordbreaksCallbackAscii( $matches)
static LocalisationCache $dataCache
listToText(array $l)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
getPluralRuleType( $number)
Find the plural rule type appropriate for the given number For example, if the language is set to Ara...
normalize( $s)
Convert a UTF-8 string to normal form C.
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
getArrow( $direction='forwards')
An arrow, depending on the language direction.
equals(Language $lang)
Compare with an other language object.
ucwordbreaksCallbackMB( $matches)
convert( $text)
convert text to different variants of a language.
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
translateBlockExpiry( $str, User $user=null, $now=0)
truncateHtml( $text, $length, $ellipsis='...')
Truncate a string of valid HTML to a specified length in bytes, appending an optional string (e....
getGrammarTransformations()
Get the grammar transformations data for the language.
__destruct()
Reduce memory usage.
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
truncate_skip(&$ret, $text, $search, $start, $len=null)
truncateHtml() helper function like strcspn() but adds the skipped chars to $ret
getFormattedNamespaces()
A convenience function that returns getNamespaces() with spaces instead of underscores in values.
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
$transformData
ReplacementArray object caches.
userTimeAndDate( $ts, User $user, array $options=[])
Get the formatted date and time for the given timestamp and formatted for the given user.
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
commafy( $number)
Adds commas to a given number.
truncate_endBracket(&$tag, $tagType, $lastCh, &$openTags)
truncateHtml() helper function (a) push or pop $tag from $openTags as needed (b) clear $tag value
firstChar( $s)
Get the first character of a string.
doMagicHook()
Run the LanguageGetMagic hook once.
$mExtendedSpecialPageAliases
getGrammarForms()
Get the grammar forms for the content language.
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
static convertDoubleWidth( $string)
convert double-width roman characters to single-width.
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
getHtmlCode()
Get the code in BCP 47 format which we can use inside of html lang="" tags.
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
static array $durationIntervals
getNsIndex( $text)
Get a namespace key by value, case insensitive.
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
LanguageConverter $mConverter
caseFold( $s)
Return a case-folded representation of $s.
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
getCode()
Get the internal language code for this language object.
static $mIranianCalendarMonthMsgs
uc( $str, $first=false)
Convert a string to uppercase.
static $mHebrewCalendarMonthMsgs
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
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()
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 ...
truncateInternal( $string, $length, $ellipsis='...', $adjustLength=true, $measureLength, $getSubstring)
Internal method used for truncation.
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
static getFallbackFor( $code)
Get the first fallback for a given language.
getWeekdayAbbreviation( $key)
isRTL()
For right-to-left language support.
separatorTransformTable()
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
replaceGrammarInNamespace( $m)
getBookstoreList()
Exports $wgBookstoreListEn.
getDir()
Return the correct HTML 'dir' attribute value for this language.
parseFormattedNumber( $number)
static factory( $code)
Get a cached or new language object for a given language code.
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
userDate( $ts, User $user, array $options=[])
Get the formatted date for the given timestamp and formatted for the given user.
getPluralRules()
Get the plural rules for the language.
autoConvertToAllVariants( $text)
convert text to all supported variants
iconv( $in, $out, $string)
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
fixVariableInNamespace( $talk)
static insertSpace( $string, $pattern)
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
static getLocalisationCache()
Get the LocalisationCache instance.
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
msg( $msg)
Get message object in this language.
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
static getJsonMessagesFileName( $code)
ucwordbreaks( $str)
capitalize words at word breaks
ucfirst( $str)
Make a string's first character uppercase.
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
userTime( $ts, User $user, array $options=[])
Get the formatted time for the given timestamp and formatted for the given user.
getNsText( $index)
Get a namespace value by key.
hasWordBreaks()
Most writing systems use whitespace to break up words.
getConvRuleTitle()
Get the conversion rule title, if any.
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
static getMessagesFileName( $code)
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
time( $ts, $adj=false, $format=true, $timecorrection=false)
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
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 getMain()
Get the RequestContext object associated with the main request.
static getSuggestedDurations( $lang=null)
Get an array of suggested block durations from MediaWiki:Ipboptions.
static isUtf8( $value)
Test whether a string is valid UTF-8.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
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
the array() calling protocol came about after MediaWiki 1.4rc1.
see documentation in includes Linker php for Linker::makeImageLink & $time
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 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
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
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
if the prop value should be in the metadata multi language array format
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Allows to change the fields on the form that will be generated $name
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
returning false will NOT prevent logging $e
if(!isset( $args[0])) $lang