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();
431 unset( $this->
$name );
447 return self::getFallbacksFor( $this->mCode );
455 return self::$dataCache->getItem( $this->mCode,
'bookstoreList' );
465 if ( is_null( $this->namespaceNames ) ) {
468 $validNamespaces = MWNamespace::getCanonicalNamespaces();
471 self::$dataCache->getItem( $this->mCode,
'namespaceNames' );
472 $this->namespaceNames += $validNamespaces;
483 # Sometimes a language will be localised but not actually exist on this wiki.
484 foreach ( $this->namespaceNames
as $key => $text ) {
485 if ( !isset( $validNamespaces[$key] ) ) {
486 unset( $this->namespaceNames[$key] );
490 # The above mixing may leave namespaces out of canonical order.
491 # Re-order by namespace ID number...
492 ksort( $this->namespaceNames );
494 Hooks::run(
'LanguageGetNamespaces', [ &$this->namespaceNames ] );
506 $this->mNamespaceIds =
null;
513 $this->namespaceNames =
null;
514 $this->mNamespaceIds =
null;
515 $this->namespaceAliases =
null;
526 foreach ( $ns
as $k => $v ) {
527 $ns[$k] = strtr( $v,
'_',
' ' );
545 return isset( $ns[$index] ) ? $ns[$index] :
false;
563 return strtr( $ns,
'_',
' ' );
578 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
580 return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->
getNsText( $index );
600 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
601 return count( $aliases ) > 0;
614 $lctext = $this->
lc( $text );
616 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
623 if ( is_null( $this->namespaceAliases ) ) {
624 $aliases = self::$dataCache->getItem( $this->mCode,
'namespaceAliases' );
628 foreach ( $aliases
as $name => $index ) {
630 unset( $aliases[
$name] );
632 $aliases[
$name] = $index;
639 (
array)self::$dataCache->getItem( $this->mCode,
'namespaceGenderAliases' );
640 foreach ( $genders
as $index => $forms ) {
641 foreach ( $forms
as $alias ) {
642 $aliases[$alias] = $index;
646 # Also add converted namespace names as aliases, to avoid confusion.
647 $convertedNames = [];
649 if ( $variant === $this->mCode ) {
653 $convertedNames[$this->
getConverter()->convertNamespace( $ns, $variant )] = $ns;
657 $this->namespaceAliases = $aliases + $convertedNames;
667 if ( is_null( $this->mNamespaceIds ) ) {
669 # Put namespace names and aliases into a hashtable.
670 # If this is too slow, then we should arrange it so that it is done
671 # before caching. The catch is that at pre-cache time, the above
672 # class-specific fixup hasn't been done.
673 $this->mNamespaceIds = [];
675 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
678 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
682 $this->mNamespaceIds[$this->
lc(
$name )] = $index;
686 return $this->mNamespaceIds;
697 $lctext = $this->
lc( $text );
698 $ns = MWNamespace::getCanonicalIndex( $lctext );
699 if ( $ns !==
null ) {
703 return isset( $ids[$lctext] ) ? $ids[$lctext] :
false;
714 $msg =
"variantname-$code";
715 if ( $usemsg &&
wfMessage( $msg )->exists() ) {
720 return $name; #
if it's defined as a language name, show that
722 # otherwise, output the language code
730 public function getDatePreferences() {
731 return self::$dataCache->getItem( $this->mCode, 'datePreferences
' );
737 function getDateFormats() {
738 return self::$dataCache->getItem( $this->mCode, 'dateFormats
' );
744 public function getDefaultDateFormat() {
745 $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat
' );
746 if ( $df === 'dmy
or mdy
' ) {
747 global $wgAmericanDates;
748 return $wgAmericanDates ? 'mdy
' : 'dmy
';
757 public function getDatePreferenceMigrationMap() {
758 return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap
' );
765 function getImageFile( $image ) {
766 return self::$dataCache->getSubitem( $this->mCode, 'imageFiles
', $image );
773 public function getImageFiles() {
774 return self::$dataCache->getItem( $this->mCode, 'imageFiles
' );
780 public function getExtraUserToggles() {
781 return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles
' );
788 function getUserToggle( $tog ) {
789 return $this->getMessageFromDB( "tog-$tog" );
803 public static function fetchLanguageNames( $inLanguage = null, $include = 'mw
' ) {
804 $cacheKey = $inLanguage === null ? 'null' : $inLanguage;
805 $cacheKey .= ":$include";
806 if ( self::$languageNameCache === null ) {
807 self::$languageNameCache = new HashBagOStuff( [ 'maxKeys
' => 20 ] );
810 $ret = self::$languageNameCache->get( $cacheKey );
812 $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
813 self::$languageNameCache->set( $cacheKey, $ret );
828 private static function fetchLanguageNamesUncached( $inLanguage = null, $include = 'mw
' ) {
829 global $wgExtraLanguageNames, $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;
1936 $gy_gannen = $gy - 1989 + 1;
1937 $gy_offset = $gy_gannen;
1938 if ( $gy_gannen == 1 ) {
1941 $gy_offset =
'平成' . $gy_offset;
1947 return [ $gy_offset, $gm, $gd ];
1964 if ( !preg_match( self::$strongDirRegex, $text,
$matches ) ) {
1982 [
'',
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X' ],
1983 [
'',
'X',
'XX',
'XXX',
'XL',
'L',
'LX',
'LXX',
'LXXX',
'XC',
'C' ],
1984 [
'',
'C',
'CC',
'CCC',
'CD',
'D',
'DC',
'DCC',
'DCCC',
'CM',
'M' ],
1985 [
'',
'M',
'MM',
'MMM',
'MMMM',
'MMMMM',
'MMMMMM',
'MMMMMMM',
1986 'MMMMMMMM',
'MMMMMMMMM',
'MMMMMMMMMM' ]
1989 $num = intval( $num );
1990 if ( $num > 10000 || $num <= 0 ) {
1995 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
1996 if ( $num >= $pow10 ) {
1997 $s .= $table[$i][(int)floor( $num / $pow10 )];
1999 $num = $num % $pow10;
2013 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ],
2014 [
'',
'י',
'כ',
'ל',
'מ',
'נ',
'ס',
'ע',
'פ',
'צ',
'ק' ],
2027 [
'',
'א',
'ב',
'ג',
'ד',
'ה',
'ו',
'ז',
'ח',
'ט',
'י' ]
2030 $num = intval( $num );
2031 if ( $num > 9999 || $num <= 0 ) {
2036 if ( $num === 1000 ) {
2038 } elseif ( $num % 1000 === 0 ) {
2039 return $table[0][$num / 1000] .
"' אלפים";
2044 for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2045 if ( $num >= $pow10 ) {
2046 if ( $num === 15 || $num === 16 ) {
2047 $letters[] = $table[0][9];
2048 $letters[] = $table[0][$num - 9];
2051 $letters = array_merge(
2053 (
array)$table[$i][intval( $num / $pow10 )]
2056 if ( $pow10 === 1000 ) {
2062 $num = $num % $pow10;
2065 $preTransformLength = count( $letters );
2066 if ( $preTransformLength === 1 ) {
2070 $lastIndex = $preTransformLength - 1;
2071 $letters[$lastIndex] = str_replace(
2072 [
'כ',
'מ',
'נ',
'פ',
'צ' ],
2073 [
'ך',
'ם',
'ן',
'ף',
'ץ' ],
2074 $letters[$lastIndex]
2080 if ( $letters[1] ===
"'" && $preTransformLength === 3 ) {
2083 array_splice( $letters, -1, 0,
'"' );
2087 return implode( $letters );
2101 if ( $tz ===
false ) {
2102 $tz =
$wgUser->getOption(
'timecorrection' );
2105 $data = explode(
'|', $tz, 3 );
2107 if ( $data[0] ==
'ZoneInfo' ) {
2109 $userTZ =
new DateTimeZone( $data[2] );
2110 $date =
new DateTime( $ts,
new DateTimeZone(
'UTC' ) );
2111 $date->setTimezone( $userTZ );
2112 return $date->format(
'YmdHis' );
2113 }
catch ( Exception
$e ) {
2115 $data[0] =
'Offset';
2119 if ( $data[0] ==
'System' || $tz ==
'' ) {
2120 # Global offset in minutes.
2122 } elseif ( $data[0] ==
'Offset' ) {
2123 $minDiff = intval( $data[1] );
2125 $data = explode(
':', $tz );
2126 if ( count( $data ) == 2 ) {
2127 $data[0] = intval( $data[0] );
2128 $data[1] = intval( $data[1] );
2129 $minDiff = abs( $data[0] ) * 60 + $data[1];
2130 if ( $data[0] < 0 ) {
2131 $minDiff = -$minDiff;
2134 $minDiff = intval( $data[0] ) * 60;
2138 # No difference ? Return time unchanged
2139 if ( 0 == $minDiff ) {
2143 MediaWiki\suppressWarnings();
2144 # Generate an adjusted date; take advantage of the fact that mktime
2145 # will normalize out-of-range values so we don't have to split $minDiff
2146 # into hours and minutes.
2148 (
int)substr( $ts, 8, 2 ) ), # Hours
2149 (
int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2150 (
int)substr( $ts, 12, 2 ), # Seconds
2151 (
int)substr( $ts, 4, 2 ), # Month
2152 (
int)substr( $ts, 6, 2 ), # Day
2153 (
int)substr( $ts, 0, 4 ) ); # Year
2155 $date =
date(
'YmdHis',
$t );
2156 MediaWiki\restoreWarnings();
2179 if ( is_bool( $usePrefs ) ) {
2181 $datePreference =
$wgUser->getDatePreference();
2183 $datePreference = (
string)User::getDefaultOption(
'date' );
2186 $datePreference = (
string)$usePrefs;
2190 if ( $datePreference ==
'' ) {
2194 return $datePreference;
2208 $wasDefault =
false;
2209 if ( $pref ==
'default' ) {
2214 if ( !isset( $this->dateFormatStrings[
$type][$pref] ) ) {
2215 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2217 if (
$type ===
'pretty' && $df ===
null ) {
2221 if ( !$wasDefault && $df ===
null ) {
2223 $df = self::$dataCache->getSubitem( $this->mCode,
'dateFormats',
"$pref $type" );
2226 $this->dateFormatStrings[
$type][$pref] = $df;
2228 return $this->dateFormatStrings[
$type][$pref];
2241 public function date( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2244 $ts = $this->
userAdjust( $ts, $timecorrection );
2260 public function time( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2263 $ts = $this->
userAdjust( $ts, $timecorrection );
2280 public function timeanddate( $ts, $adj =
false, $format =
true, $timecorrection =
false ) {
2283 $ts = $this->
userAdjust( $ts, $timecorrection );
2304 foreach ( $intervals
as $intervalName => $intervalValue ) {
2307 $message =
wfMessage(
'duration-' . $intervalName )->numParams( $intervalValue );
2308 $segments[] = $message->inLanguage( $this )->escaped();
2326 if ( empty( $chosenIntervals ) ) {
2327 $chosenIntervals = [
2339 $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2340 $sortedNames = array_keys( $intervals );
2341 $smallestInterval = array_pop( $sortedNames );
2345 foreach ( $intervals
as $name => $length ) {
2346 $value = floor( $seconds / $length );
2348 if (
$value > 0 || (
$name == $smallestInterval && empty( $segments ) ) ) {
2349 $seconds -=
$value * $length;
2378 $options += [
'timecorrection' =>
true,
'format' =>
true ];
2379 if (
$options[
'timecorrection'] !==
false ) {
2380 if (
$options[
'timecorrection'] ===
true ) {
2381 $offset =
$user->getOption(
'timecorrection' );
2383 $offset =
$options[
'timecorrection'];
2387 if (
$options[
'format'] ===
true ) {
2388 $format =
$user->getDatePreference();
2483 if ( $relativeTo ===
null ) {
2486 if (
$user ===
null ) {
2492 $offsetRel = $relativeTo->offsetForUser(
$user );
2495 if ( Hooks::run(
'GetHumanTimestamp', [ &$ts,
$time, $relativeTo,
$user, $this ] ) ) {
2500 $time->timestamp->sub( $offsetThis );
2501 $relativeTo->timestamp->sub( $offsetRel );
2520 $diff = $ts->diff( $relativeTo );
2521 $diffDay = (bool)( (
int)$ts->timestamp->format(
'w' ) -
2522 (int)$relativeTo->timestamp->format(
'w' ) );
2523 $days = $diff->days ?: (int)$diffDay;
2524 if ( $diff->invert || $days > 5
2525 && $ts->timestamp->format(
'Y' ) !== $relativeTo->timestamp->format(
'Y' )
2533 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2534 } elseif ( $days > 5 ) {
2537 $ts = $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2538 } elseif ( $days > 1 ) {
2541 $weekday = self::$mWeekdayMsgs[$ts->timestamp->format(
'w' )];
2545 ->inLanguage( $this )
2546 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2548 } elseif ( $days == 1 ) {
2552 ->inLanguage( $this )
2553 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2555 } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2559 ->inLanguage( $this )
2560 ->params( $this->
sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2565 } elseif ( $diff->h == 1 ) {
2567 $ts =
wfMessage(
'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2568 } elseif ( $diff->i >= 1 ) {
2570 $ts =
wfMessage(
'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2571 } elseif ( $diff->s >= 30 ) {
2573 $ts =
wfMessage(
'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2587 return self::$dataCache->getSubitem( $this->mCode,
'messages', $key );
2594 return self::$dataCache->getItem( $this->mCode,
'messages' );
2605 # *input* string. We just ignore those too.
2608 MediaWiki\suppressWarnings();
2609 $text =
iconv( $in,
$out .
'//IGNORE', $string );
2610 MediaWiki\restoreWarnings();
2629 return mb_strtoupper(
$matches[0] );
2637 return mb_strtoupper(
$matches[0] );
2651 } elseif ( $o < 128 ) {
2655 return $this->
uc( $str,
true );
2667 public function uc( $str, $first =
false ) {
2670 return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2675 return $this->
isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2686 return strval( $str );
2687 } elseif ( $o >= 128 ) {
2688 return $this->
lc( $str,
true );
2689 } elseif ( $o > 96 ) {
2692 $str[0] = strtolower( $str[0] );
2702 function lc( $str, $first =
false ) {
2705 return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2707 return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2710 return $this->
isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2719 return strlen( $str ) !== mb_strlen( $str );
2728 $str = $this->
lc( $str );
2731 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2734 return preg_replace_callback(
2736 [ $this,
'ucwordsCallbackMB' ],
2740 return ucwords( strtolower( $str ) );
2752 $str = $this->
lc( $str );
2755 $breaks =
"[ \-\(\)\}\{\.,\?!]";
2758 $replaceRegexp =
"/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2759 "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2761 return preg_replace_callback(
2763 [ $this,
'ucwordbreaksCallbackMB' ],
2767 return preg_replace_callback(
2768 '/\b([\w\x80-\xff]+)\b/',
2769 [ $this,
'ucwordbreaksCallbackAscii' ],
2791 return $this->
uc(
$s );
2800 if ( is_array(
$s ) ) {
2801 throw new MWException(
'Given array to checkTitleEncoding.' );
2814 return self::$dataCache->getItem( $this->mCode,
'fallback8bitEncoding' );
2848 return self::convertDoubleWidth( $string );
2860 static $full =
null;
2861 static $half =
null;
2863 if ( $full ===
null ) {
2864 $fullWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2865 $halfWidth =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2866 $full = str_split( $fullWidth, 3 );
2867 $half = str_split( $halfWidth );
2870 $string = str_replace( $full, $half, $string );
2880 $string = preg_replace( $pattern,
" $1 ", $string );
2881 $string = preg_replace(
'/ +/',
' ', $string );
2890 # some languages, e.g. Chinese, need to do a conversion
2891 # in order for search results to be displayed correctly
2904 '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2905 '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2911 if ( strlen(
$matches[1] ) != 3 ) {
2919 } elseif (
$code < 0xb098 ) {
2920 return "\xe3\x84\xb1";
2921 } elseif (
$code < 0xb2e4 ) {
2922 return "\xe3\x84\xb4";
2923 } elseif (
$code < 0xb77c ) {
2924 return "\xe3\x84\xb7";
2925 } elseif (
$code < 0xb9c8 ) {
2926 return "\xe3\x84\xb9";
2927 } elseif (
$code < 0xbc14 ) {
2928 return "\xe3\x85\x81";
2929 } elseif (
$code < 0xc0ac ) {
2930 return "\xe3\x85\x82";
2931 } elseif (
$code < 0xc544 ) {
2932 return "\xe3\x85\x85";
2933 } elseif (
$code < 0xc790 ) {
2934 return "\xe3\x85\x87";
2935 } elseif (
$code < 0xcc28 ) {
2936 return "\xe3\x85\x88";
2937 } elseif (
$code < 0xce74 ) {
2938 return "\xe3\x85\x8a";
2939 } elseif (
$code < 0xd0c0 ) {
2940 return "\xe3\x85\x8b";
2941 } elseif (
$code < 0xd30c ) {
2942 return "\xe3\x85\x8c";
2943 } elseif (
$code < 0xd558 ) {
2944 return "\xe3\x85\x8d";
2946 return "\xe3\x85\x8e";
2991 $s = UtfNormal\Validator::cleanUp(
$s );
3015 if ( !isset( $this->transformData[$file] ) ) {
3017 if ( $data ===
false ) {
3018 throw new MWException( __METHOD__ .
": The transformation file $file is missing" );
3022 return $this->transformData[$file]->replace( $string );
3031 return self::$dataCache->getItem( $this->mCode,
'rtl' );
3039 return $this->
isRTL() ?
'rtl' :
'ltr';
3051 return $this->
isRTL() ?
'right' :
'left';
3063 return $this->
isRTL() ?
'left' :
'right';
3079 return $this->
isRTL() ?
'‎' :
'‏';
3081 return $this->
isRTL() ?
'‏' :
'‎';
3095 $lrm =
"\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3096 $rlm =
"\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3098 return $this->
isRTL() ? $lrm : $rlm;
3100 return $this->
isRTL() ? $rlm : $lrm;
3107 return self::$dataCache->getItem( $this->mCode,
'capitalizeAllNouns' );
3118 switch ( $direction ) {
3120 return $this->
isRTL() ?
'←' :
'→';
3122 return $this->
isRTL() ?
'→' :
'←';
3140 return self::$dataCache->getItem( $this->mCode,
'linkPrefixExtension' );
3148 return self::$dataCache->getItem( $this->mCode,
'magicWords' );
3155 if ( $this->mMagicHookDone ) {
3158 $this->mMagicHookDone =
true;
3159 Hooks::run(
'LanguageGetMagic', [ &$this->mMagicExtensions, $this->
getCode() ] );
3169 if ( !$this->mMagicHookDone ) {
3173 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3174 $rawEntry = $this->mMagicExtensions[$mw->mId];
3176 $rawEntry = self::$dataCache->getSubitem(
3177 $this->mCode,
'magicWords', $mw->mId );
3180 if ( !is_array( $rawEntry ) ) {
3181 wfWarn(
"\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3183 $mw->mCaseSensitive = $rawEntry[0];
3184 $mw->mSynonyms = array_slice( $rawEntry, 1 );
3195 $fallbackChain = array_reverse( $fallbackChain );
3196 foreach ( $fallbackChain
as $code ) {
3197 if ( isset( $newWords[
$code] ) ) {
3198 $this->mMagicExtensions = $newWords[
$code] + $this->mMagicExtensions;
3210 if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3212 $this->mExtendedSpecialPageAliases =
3213 self::$dataCache->getItem( $this->mCode,
'specialPageAliases' );
3214 Hooks::run(
'LanguageGetSpecialPageAliases',
3215 [ &$this->mExtendedSpecialPageAliases, $this->
getCode() ] );
3218 return $this->mExtendedSpecialPageAliases;
3228 return "<em>$text</em>";
3255 if ( !$nocommafy ) {
3256 $number = $this->
commafy( $number );
3259 $number = strtr( $number,
$s );
3266 $number = strtr( $number,
$s );
3282 return $this->
formatNum( $number,
true );
3293 $s = array_filter(
$s );
3294 $number = strtr( $number, array_flip(
$s ) );
3300 $s = array_filter(
$s );
3301 $number = strtr( $number, array_flip(
$s ) );
3304 $number = strtr( $number, [
',' =>
'' ] );
3316 if ( $number ===
null ) {
3322 return strrev( (
string)preg_replace(
'/(\d{3})(?=\d)(?!\d*\.)/',
'$1,', strrev( $number ) ) );
3326 if ( intval( $number ) < 0 ) {
3329 $number = substr( $number, 1 );
3334 preg_match(
"/\d+/", $number, $integerPart );
3335 preg_match(
"/\.\d*/", $number, $decimalPart );
3336 $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] :
"";
3337 if ( $groupedNumber === $number ) {
3339 return $sign . $groupedNumber;
3341 $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3342 while ( $start > 0 ) {
3343 $match =
$matches[0][$numMatches - 1];
3344 $matchLen = strlen( $match );
3345 $start = $end - $matchLen;
3349 $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3351 if ( $numMatches > 1 ) {
3356 $groupedNumber =
"," . $groupedNumber;
3359 return $sign . $groupedNumber;
3367 return self::$dataCache->getItem( $this->mCode,
'digitGroupingPattern' );
3374 return self::$dataCache->getItem( $this->mCode,
'digitTransformTable' );
3381 return self::$dataCache->getItem( $this->mCode,
'separatorTransformTable' );
3394 $m = count( $l ) - 1;
3399 $and = $this->
msg(
'and' )->escaped();
3400 $space = $this->
msg(
'word-separator' )->escaped();
3402 $comma = $this->
msg(
'comma-separator' )->escaped();
3406 for ( $i = $m - 1; $i >= 0; $i-- ) {
3407 if ( $i == $m - 1 ) {
3408 $s = $l[$i] . $and . $space .
$s;
3410 $s = $l[$i] . $comma .
$s;
3424 wfMessage(
'comma-separator' )->inLanguage( $this )->escaped(),
3437 wfMessage(
'semicolon-separator' )->inLanguage( $this )->escaped(),
3449 wfMessage(
'pipe-separator' )->inLanguage( $this )->escaped(),
3471 function truncate( $string, $length, $ellipsis =
'...', $adjustLength =
true ) {
3472 # Use the localized ellipsis character
3473 if ( $ellipsis ==
'...' ) {
3474 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3476 # Check if there is no need to truncate
3477 if ( $length == 0 ) {
3479 } elseif ( strlen( $string ) <= abs( $length ) ) {
3482 $stringOriginal = $string;
3483 # If ellipsis length is >= $length then we can't apply $adjustLength
3484 if ( $adjustLength && strlen( $ellipsis ) >= abs( $length ) ) {
3485 $string = $ellipsis;
3486 # Otherwise, truncate and add ellipsis...
3488 $eLength = $adjustLength ? strlen( $ellipsis ) : 0;
3489 if ( $length > 0 ) {
3490 $length -= $eLength;
3491 $string = substr( $string, 0, $length );
3493 $string = rtrim( $string );
3494 $string = $string . $ellipsis;
3496 $length += $eLength;
3497 $string = substr( $string, $length );
3499 $string = ltrim( $string );
3500 $string = $ellipsis . $string;
3503 # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3504 # This check is *not* redundant if $adjustLength, due to the single case where
3505 # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3506 if ( strlen( $string ) < strlen( $stringOriginal ) ) {
3509 return $stringOriginal;
3521 if ( $string !=
'' ) {
3522 $char = ord( $string[strlen( $string ) - 1] );
3524 if ( $char >= 0xc0 ) {
3525 # We got the first byte only of a multibyte char; remove it.
3526 $string = substr( $string, 0, -1 );
3527 } elseif ( $char >= 0x80 &&
3529 preg_match(
'/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3530 '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3532 # We chopped in the middle of a character; remove it
3547 if ( $string !=
'' ) {
3548 $char = ord( $string[0] );
3549 if ( $char >= 0x80 && $char < 0xc0 ) {
3550 # We chopped in the middle of a character; remove the whole thing
3551 $string = preg_replace(
'/^[\x80-\xbf]+/',
'', $string );
3573 # Use the localized ellipsis character
3574 if ( $ellipsis ==
'...' ) {
3575 $ellipsis =
wfMessage(
'ellipsis' )->inLanguage( $this )->escaped();
3577 # Check if there is clearly no need to truncate
3578 if ( $length <= 0 ) {
3580 } elseif ( strlen( $text ) <= $length ) {
3585 $testingEllipsis =
false;
3593 $textLen = strlen( $text );
3594 $neLength = max( 0, $length - strlen( $ellipsis ) );
3595 for ( $pos = 0;
true; ++$pos ) {
3596 # Consider truncation once the display length has reached the maximim.
3597 # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3598 # Check that we're not in the middle of a bracket/entity...
3599 if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3600 if ( !$testingEllipsis ) {
3601 $testingEllipsis =
true;
3602 # Save where we are; we will truncate here unless there turn out to
3603 # be so few remaining characters that truncation is not necessary.
3604 if ( !$maybeState ) {
3605 $maybeState = [
$ret, $openTags ];
3607 } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3608 # String in fact does need truncation, the truncation point was OK.
3609 list(
$ret, $openTags ) = $maybeState;
3615 if ( $pos >= $textLen ) {
3619 # Read the next char...
3621 $lastCh = $pos ? $text[$pos - 1] :
'';
3627 } elseif ( $ch ==
'>' ) {
3631 } elseif ( $bracketState == 1 ) {
3639 } elseif ( $bracketState == 2 ) {
3646 } elseif ( $bracketState == 0 ) {
3647 if ( $entityState ) {
3653 if ( $neLength == 0 && !$maybeState ) {
3656 $maybeState = [ substr(
$ret, 0, -1 ), $openTags ];
3663 $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3665 $dispLen += $skipped;
3673 while ( count( $openTags ) > 0 ) {
3674 $ret .=
'</' . array_pop( $openTags ) .
'>';
3691 if ( $len ===
null ) {
3693 } elseif ( $len < 0 ) {
3697 if ( $start < strlen( $text ) ) {
3698 $skipCount = strcspn( $text, $search, $start, $len );
3699 $ret .= substr( $text, $start, $skipCount );
3714 $tag = ltrim( $tag );
3716 if ( $tagType == 0 && $lastCh !=
'/' ) {
3718 } elseif ( $tagType == 1 ) {
3719 if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3720 array_pop( $openTags );
3749 if ( is_string( $forms ) ) {
3753 foreach ( array_values( $forms )
as $rule ) {
3756 if ( $form ===
'@metadata' ) {
3760 $replacement = $rule[1];
3762 $regex =
'/' . addcslashes( $form,
'/' ) .
'/u';
3763 $patternMatches = preg_match( $regex, $word );
3765 if ( $patternMatches ===
false ) {
3767 'An error occurred while processing grammar. ' .
3768 "Word: '$word'. Regex: /$form/."
3770 } elseif ( $patternMatches === 1 ) {
3771 $word = preg_replace( $regex, $replacement, $word );
3807 $languageCode = $this->
getCode();
3809 if ( self::$grammarTransformations ===
null ) {
3810 self::$grammarTransformations =
new MapCacheLRU( 10 );
3813 if ( self::$grammarTransformations->has( $languageCode ) ) {
3814 return self::$grammarTransformations->get( $languageCode );
3819 $grammarDataFile = __DIR__ .
"/data/grammarTransformations/$languageCode.json";
3820 if ( is_readable( $grammarDataFile ) ) {
3821 $data = FormatJson::decode(
3822 file_get_contents( $grammarDataFile ),
3826 if ( $data ===
null ) {
3827 throw new MWException(
"Invalid grammar data for \"$languageCode\"." );
3830 self::$grammarTransformations->set( $languageCode, $data );
3856 if ( !count( $forms ) ) {
3860 if ( $gender ===
'male' ) {
3863 if ( $gender ===
'female' ) {
3866 return isset( $forms[2] ) ? $forms[2] : $forms[0];
3887 if ( is_string( $forms ) ) {
3890 if ( !count( $forms ) ) {
3895 $pluralForm = min( $pluralForm, count( $forms ) - 1 );
3896 return $forms[$pluralForm];
3915 foreach ( $forms
as $index => $form ) {
3916 if ( preg_match(
'/\d+=/i', $form ) ) {
3917 $pos = strpos( $form,
'=' );
3918 if ( substr( $form, 0, $pos ) === (
string)$count ) {
3919 return substr( $form, $pos + 1 );
3921 unset( $forms[$index] );
3924 return array_values( $forms );
3936 while ( count( $forms ) < $count ) {
3937 $forms[] = $forms[count( $forms ) - 1];
3959 $dir = self::strongDirFromContent( $text );
3960 if ( $dir ===
'ltr' ) {
3962 return self::$lre . $text . self::$pdf;
3964 if ( $dir ===
'rtl' ) {
3966 return self::$rle . $text . self::$pdf;
3987 foreach ( $duration
as $show =>
$value ) {
3988 if ( strcmp( $str,
$value ) == 0 ) {
3989 return htmlspecialchars( trim( $show ) );
3994 foreach ( $duration
as $show =>
$value ) {
3996 return htmlspecialchars( trim( $show ) );
4002 $time = strtotime( $str, $now );
4003 if (
$time ===
false ) {
4005 } elseif (
$time !== strtotime( $str, $now + 1 ) ) {
4010 if (
$time === 0 ) {
4012 $time =
'19700101000000';
4049 return $this->mConverter;
4059 return $this->mConverter->autoConvertToAllVariants( $text );
4069 return $this->mConverter->convert( $text );
4079 return $this->mConverter->convertTitle(
$title );
4089 return $this->mConverter->convertNamespace( $ns );
4109 return (
bool)$this->mConverter->validateVariant( $variant );
4120 return htmlspecialchars( $this->
convert( $text, $isTitle ) );
4128 return $this->mConverter->convertCategoryKey( $key );
4138 return $this->mConverter->getVariants();
4145 return $this->mConverter->getPreferredVariant();
4152 return $this->mConverter->getDefaultVariant();
4159 return $this->mConverter->getURLVariant();
4175 $this->mConverter->findVariantLink(
$link, $nt, $ignoreOtherCond );
4185 return $this->mConverter->getExtraHashOptions();
4196 return $this->mConverter->getParsedTitle();
4206 $this->mConverter->updateConversionTable(
$title );
4224 return $this->mConverter->markNoConversion( $text );
4237 return self::$dataCache->getItem( $this->mCode,
'linkTrail' );
4247 return self::$dataCache->getItem( $this->mCode,
'linkPrefixCharset' );
4258 if ( $this->mParentLanguage !==
false ) {
4259 return $this->mParentLanguage;
4264 $this->mParentLanguage =
null;
4268 if ( !
$lang->hasVariant( $this->getCode() ) ) {
4269 $this->mParentLanguage =
null;
4273 $this->mParentLanguage =
$lang;
4285 return $lang->getCode() === $this->mCode;
4297 return $this->mCode;
4311 if ( is_null( $this->mHtmlCode ) ) {
4314 return $this->mHtmlCode;
4321 $this->mCode =
$code;
4323 $this->mHtmlCode =
null;
4324 $this->mParentLanguage =
false;
4336 preg_match(
'/' . preg_quote( $prefix,
'/' ) .
'([A-Z][a-z_]+)' .
4337 preg_quote( $suffix,
'/' ) .
'/', $filename, $m );
4338 if ( !count( $m ) ) {
4341 return str_replace(
'_',
'-', strtolower( $m[1] ) );
4353 return 'Language' . str_replace(
'-',
'_',
ucfirst(
$code ) );
4366 if ( !self::isValidBuiltInCode(
$code ) ) {
4367 throw new MWException(
"Invalid language code \"$code\"" );
4370 return $prefix . str_replace(
'-',
'_',
ucfirst(
$code ) ) . $suffix;
4379 $file = self::getFileName(
"$IP/languages/messages/Messages",
$code,
'.php' );
4380 Hooks::run(
'Language::getMessagesFileName', [
$code, &$file ] );
4393 if ( !self::isValidBuiltInCode(
$code ) ) {
4394 throw new MWException(
"Invalid language code \"$code\"" );
4397 return "$IP/languages/i18n/$code.json";
4408 $fallbacks = self::getFallbacksFor(
$code );
4410 return $fallbacks[0];
4423 if (
$code ===
'en' || !self::isValidBuiltInCode(
$code ) ) {
4428 return self::getLocalisationCache()->getItem(
$code,
'fallbackSequence' ) ?: [
'en' ];
4444 $cacheKey =
"{$code}-{$wgLanguageCode}";
4446 if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4447 $fallbacks = self::getFallbacksFor(
$code );
4454 $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4456 self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4458 return self::$fallbackLanguageCache[$cacheKey];
4471 return self::getLocalisationCache()->getItem(
$code,
'messages' );
4483 return self::getLocalisationCache()->getSubitem(
$code,
'messages', $key );
4495 return self::getLocalisationCache()->getSubitemList(
$code,
'messages' );
4503 if ( strpos( $talk,
'$1' ) ===
false ) {
4510 # Allow grammar transformations
4511 # Allowing full message-style parsing would make simple requests
4512 # such as action=raw much more expensive than they need to be.
4513 # This will hopefully cover most cases.
4514 $talk = preg_replace_callback(
'/{{grammar:(.*?)\|(.*?)}}/i',
4515 [ $this,
'replaceGrammarInNamespace' ], $talk );
4516 return str_replace(
' ',
'_', $talk );
4537 public function formatExpiry( $expiry, $format =
true, $infinity =
'infinity' ) {
4539 if ( $dbInfinity ===
null ) {
4543 if ( $expiry ==
'' || $expiry ===
'infinity' || $expiry == $dbInfinity ) {
4544 return $format ===
true
4548 return $format ===
true
4568 if ( !is_array( $format ) ) {
4569 $format = [
'avoid' => $format ];
4571 if ( !isset( $format[
'avoid'] ) ) {
4572 $format[
'avoid'] =
false;
4574 if ( !isset( $format[
'noabbrevs'] ) ) {
4575 $format[
'noabbrevs'] =
false;
4578 $format[
'noabbrevs'] ?
'seconds' :
'seconds-abbrev' )->inLanguage( $this );
4580 $format[
'noabbrevs'] ?
'minutes' :
'minutes-abbrev' )->inLanguage( $this );
4582 $format[
'noabbrevs'] ?
'hours' :
'hours-abbrev' )->inLanguage( $this );
4584 $format[
'noabbrevs'] ?
'days' :
'days-abbrev' )->inLanguage( $this );
4586 if ( round( $seconds * 10 ) < 100 ) {
4587 $s = $this->
formatNum( sprintf(
"%.1f", round( $seconds * 10 ) / 10 ) );
4588 $s = $secondsMsg->params(
$s )->text();
4589 } elseif ( round( $seconds ) < 60 ) {
4591 $s = $secondsMsg->params(
$s )->text();
4592 } elseif ( round( $seconds ) < 3600 ) {
4593 $minutes = floor( $seconds / 60 );
4594 $secondsPart = round( fmod( $seconds, 60 ) );
4595 if ( $secondsPart == 60 ) {
4599 $s = $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4601 $s .= $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4602 } elseif ( round( $seconds ) <= 2 * 86400 ) {
4603 $hours = floor( $seconds / 3600 );
4604 $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4605 $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4606 if ( $secondsPart == 60 ) {
4610 if ( $minutes == 60 ) {
4614 $s = $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4616 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4617 if ( !in_array( $format[
'avoid'], [
'avoidseconds',
'avoidminutes' ] ) ) {
4618 $s .=
' ' . $secondsMsg->params( $this->
formatNum( $secondsPart ) )->text();
4621 $days = floor( $seconds / 86400 );
4622 if ( $format[
'avoid'] ===
'avoidminutes' ) {
4623 $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4624 if ( $hours == 24 ) {
4628 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4630 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4631 } elseif ( $format[
'avoid'] ===
'avoidseconds' ) {
4632 $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4633 $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4634 if ( $minutes == 60 ) {
4638 if ( $hours == 24 ) {
4642 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4644 $s .= $hoursMsg->params( $this->
formatNum( $hours ) )->text();
4646 $s .= $minutesMsg->params( $this->
formatNum( $minutes ) )->text();
4648 $s = $daysMsg->params( $this->
formatNum( $days ) )->text();
4679 return str_replace(
'$1', $this->
formatNum( $size ),
4683 $sizes = [
'',
'kilo',
'mega',
'giga',
'tera',
'peta',
'exa',
'zeta',
'yotta' ];
4686 $maxIndex = count( $sizes ) - 1;
4687 while ( $size >= $boundary && $index < $maxIndex ) {
4698 $msg = str_replace(
'$1', $sizes[$index], $messageKey );
4700 $size = round( $size, $round );
4702 return str_replace(
'$1', $this->
formatNum( $size ), $text );
4733 $dirmark = ( $oppositedm ? $this->
getDirMark(
true ) :
'' ) . $this->getDirMark();
4737 $this->
msg(
'word-separator' )->escaped() .
4738 $this->
msg(
'parentheses' )->rawParams( $details )->escaped();
4756 # Make 'previous' link
4757 $prev =
wfMessage(
'prevn' )->inLanguage( $this )->title(
$title )->numParams( $limit )->text();
4758 if ( $offset > 0 ) {
4759 $plink = $this->
numLink(
$title, max( $offset - $limit, 0 ), $limit,
4760 $query, $prev,
'prevn-title',
'mw-prevlink' );
4762 $plink = htmlspecialchars( $prev );
4766 $next =
wfMessage(
'nextn' )->inLanguage( $this )->title(
$title )->numParams( $limit )->text();
4768 $nlink = htmlspecialchars( $next );
4771 $query, $next,
'nextn-title',
'mw-nextlink' );
4774 # Make links to set number of items per page
4776 foreach ( [ 20, 50, 100, 250, 500 ]
as $num ) {
4781 return wfMessage(
'viewprevnext' )->inLanguage( $this )->title(
$title
4782 )->rawParams( $plink, $nlink, $this->
pipeList( $numLinks ) )->escaped();
4800 $query = [
'limit' => $limit,
'offset' => $offset ] +
$query;
4801 $tooltip =
wfMessage( $tooltipMsg )->inLanguage( $this )->title(
$title )
4802 ->numParams( $limit )->text();
4804 return Html::element(
'a', [
'href' =>
$title->getLocalURL(
$query ),
4805 'title' => $tooltip,
'class' => $class ],
$link );
4814 return $this->mConverter->getConvRuleTitle();
4823 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'compiledPluralRules' );
4824 $fallbacks = self::getFallbacksFor( $this->mCode );
4825 if ( !$pluralRules ) {
4826 foreach ( $fallbacks
as $fallbackCode ) {
4827 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'compiledPluralRules' );
4828 if ( $pluralRules ) {
4833 return $pluralRules;
4842 $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRules' );
4843 $fallbacks = self::getFallbacksFor( $this->mCode );
4844 if ( !$pluralRules ) {
4845 foreach ( $fallbacks
as $fallbackCode ) {
4846 $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRules' );
4847 if ( $pluralRules ) {
4852 return $pluralRules;
4861 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ),
'pluralRuleTypes' );
4862 $fallbacks = self::getFallbacksFor( $this->mCode );
4863 if ( !$pluralRuleTypes ) {
4864 foreach ( $fallbacks
as $fallbackCode ) {
4865 $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ),
'pluralRuleTypes' );
4866 if ( $pluralRuleTypes ) {
4871 return $pluralRuleTypes;
4881 $form = Evaluator::evaluateCompiled( $number, $pluralRules );
4896 if ( isset( $pluralRuleTypes[$index] ) ) {
4897 return $pluralRuleTypes[$index];
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgLanguageCode
Site language code.
$wgExtraGenderNamespaces
Same as above, but for namespaces with gender distinction.
$wgTranslateNumerals
For Hindi and Arabic use local numerals instead of Western style (0-9) numerals in interface.
$wgGrammarForms
Some languages need different word forms, usually for different cases.
$wgExtraNamespaces
Additional namespaces.
$wgAllUnicodeFixes
Set this to always convert certain Unicode sequences to modern ones regardless of the content languag...
$wgDummyLanguageCodes
Functionally the same as $wgExtraLanguageCodes, but deprecated.
$wgNamespaceAliases
Namespace aliases.
$wgMetaNamespace
Name of the project namespace.
$wgMetaNamespaceTalk
Name of the project talk namespace.
$wgLocalTZoffset
Set an offset from UTC in minutes to use for the default timezone setting for anonymous users and new...
$wgLocalisationCacheConf
Localisation cache configuration.
$wgLangObjCacheSize
Language cache size, or really how many languages can we handle simultaneously without degrading to c...
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
A fake language variant converter.
Simple store for keeping values in an associative array for the current process.
Base class for language conversion.
static array $languagesWithVariants
languages supporting variants
Internationalisation code.
hasVariants()
Check if this is a language with variants.
initContLang()
Hook which will be called if this is the content language.
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
date( $ts, $adj=false, $format=true, $timecorrection=false)
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
static $strongDirRegex
Directionality test regex for embedBidi().
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
getPluralRuleTypes()
Get the plural rule types for the language.
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
convertCategoryKey( $key)
array null $namespaceNames
static getMessageKeysFor( $code)
Get all message keys for a given language.
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
static $mHebrewCalendarMonthGenMsgs
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
getHebrewCalendarMonthName( $key)
static getMessageFor( $key, $code)
Get a message for a given language.
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
static getFallbacksFor( $code)
Get the ordered list of fallback languages.
static array $fallbackLanguageCache
Cache for language fallbacks.
getMonthAbbreviation( $key)
getHebrewCalendarMonthNameGen( $key)
static $lre
Unicode directional formatting characters, for embedBidi()
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
hasVariant( $variant)
Check if the language has the specific variant.
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.
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.
getIranianCalendarMonthName( $key)
getHumanTimestampInternal(MWTimestamp $ts, MWTimestamp $relativeTo, User $user)
Convert an MWTimestamp into a pretty human-readable timestamp using the given user preferences and re...
getHumanTimestamp(MWTimestamp $time, MWTimestamp $relativeTo=null, User $user=null)
Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
preConvertPlural($forms, $count)
Checks that convertPlural was given an array and pads it to requested amount of forms by copying the ...
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
static getFallbackFor( $code)
Get the first fallback for a given language.
getWeekdayAbbreviation( $key)
isRTL()
For right-to-left language support.
separatorTransformTable()
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
replaceGrammarInNamespace( $m)
getBookstoreList()
Exports $wgBookstoreListEn.
getDir()
Return the correct HTML 'dir' attribute value for this language.
parseFormattedNumber( $number)
static factory( $code)
Get a cached or new language object for a given language code.
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
userDate( $ts, User $user, array $options=[])
Get the formatted date for the given timestamp and formatted for the given user.
getPluralRules()
Get the plural rules for the language.
autoConvertToAllVariants( $text)
convert text to all supported variants
iconv( $in, $out, $string)
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
fixVariableInNamespace( $talk)
static insertSpace( $string, $pattern)
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
convertNamespace( $ns)
Convert a namespace index to a string in the preferred variant.
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
static getLocalisationCache()
Get the LocalisationCache instance.
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
msg( $msg)
Get message object in this language.
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
static getJsonMessagesFileName( $code)
ucwordbreaks( $str)
capitalize words at word breaks
ucfirst( $str)
Make a string's first character uppercase.
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
userTime( $ts, User $user, array $options=[])
Get the formatted time for the given timestamp and formatted for the given user.
getNsText( $index)
Get a namespace value by key.
hasWordBreaks()
Most writing systems use whitespace to break up words.
getConvRuleTitle()
Get the conversion rule title, if any.
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
static getMessagesFileName( $code)
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
time( $ts, $adj=false, $format=true, $timecorrection=false)
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
static $mHijriCalendarMonthMsgs
static $mWeekdayAbbrevMsgs
unsegmentForDiff( $text)
and unsegment to show the result
formatComputingNumbers( $size, $boundary, $messageKey)
ucwordsCallbackMB( $matches)
getPluralRuleIndexNumber( $number)
Find the index number of the plural rule appropriate for the given number.
updateConversionTable(Title $title)
Refresh the cache of conversion tables when MediaWiki:Conversiontable* is updated.
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
convertHtml( $text, $isTitle=false)
Perform output conversion on a string, and encode for safe HTML output.
getConverter()
Return the LanguageConverter used in the Language.
static tsToHijri( $ts)
Converting Gregorian dates to Hijri dates.
static isValidCode( $code)
Returns true if a language code string is of a valid form, whether or not it exists.
static 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()
Static methods.
static getSuggestedDurations( $lang=null)
Get an array of suggested block durations from MediaWiki:Ipboptions.
static isUtf8( $value)
Test whether a string is valid UTF-8.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
=Architecture==Two class hierarchies are used to provide the functionality associated with the different content models:*Content interface(and AbstractContent base class) define functionality that acts on the concrete content of a page, and *ContentHandler base class provides functionality specific to a content model, but not acting on concrete content. The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content. These Content objects are to be used by MediaWiki everywhere, instead of passing page content around as text. All manipulation and analysis of page content must be done via the appropriate methods of the Content object. For each content model, a subclass of ContentHandler has to be registered with $wgContentHandlers. The ContentHandler object for a given content model can be obtained using ContentHandler::getForModelID($id). Also Title, WikiPage and Revision now have getContentHandler() methods for convenience. ContentHandler objects are singletons that provide functionality specific to the content type, but not directly acting on the content of some page. ContentHandler::makeEmptyContent() and ContentHandler::unserializeContent() can be used to create a Content object of the appropriate type. However, it is recommended to instead use WikiPage::getContent() resp. Revision::getContent() to get a page 's content as a Content object. These two methods should be the ONLY way in which page content is accessed. Another important function of ContentHandler objects is to define custom action handlers for a content model, see ContentHandler::getActionOverrides(). This is similar to what WikiPage::getActionOverrides() was already doing.==Serialization==With the ContentHandler facility, page content no longer has to be text based. Objects implementing the Content interface are used to represent and handle the content internally. For storage and data exchange, each content model supports at least one serialization format via ContentHandler::serializeContent($content). The list of supported formats for a given content model can be accessed using ContentHandler::getSupportedFormats(). Content serialization formats are identified using MIME type like strings. The following formats are built in:*text/x-wiki - wikitext *text/javascript - for js pages *text/css - for css pages *text/plain - for future use, e.g. with plain text messages. *text/html - for future use, e.g. with plain html messages. *application/vnd.php.serialized - for future use with the api and for extensions *application/json - for future use with the api, and for use by extensions *application/xml - for future use with the api, and for use by extensions In PHP, use the corresponding CONTENT_FORMAT_XXX constant. Note that when using the API to access page content, especially action=edit, action=parse and action=query &prop=revisions, the model and format of the content should always be handled explicitly. Without that information, interpretation of the provided content is not reliable. The same applies to XML dumps generated via maintenance/dumpBackup.php or Special:Export. Also note that the API will provide encapsulated, serialized content - so if the API was called with format=json, and contentformat is also json(or rather, application/json), the page content is represented as a string containing an escaped json structure. Extensions that use JSON to serialize some types of page content may provide specialized API modules that allow access to that content in a more natural form.==Compatibility==The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least for pages that contain wikitext or other text based content. However, a number of functions and hooks have been deprecated in favor of new versions that are aware of the page 's content model, and will now generate warnings when used. Most importantly, the following functions have been deprecated:*Revisions::getText() is deprecated in favor Revisions::getContent() *WikiPage::getText() is deprecated in favor WikiPage::getContent() Also, the old Article::getContent()(which returns text) is superceded by Article::getContentObject(). However, both methods should be avoided since they do not provide clean access to the page 's actual content. For instance, they may return a system message for non-existing pages. Use WikiPage::getContent() instead. Code that relies on a textual representation of the page content should eventually be rewritten. However, ContentHandler::getContentText() provides a stop-gap that can be used to get text for a page. Its behavior is controlled by $wgContentHandlerTextFallback it
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
when a variable name is used in a it is silently declared as a new local masking the global
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
database rows
the array() calling protocol came about after MediaWiki 1.4rc1.
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
namespace and then decline to actually register it file or subcat img or subcat $title
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
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
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
returning false will NOT prevent logging $e
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring which defines all default service and specifies how they depend on each other("wiring"). When a new service is added to MediaWiki core
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
if(!isset( $args[0])) $lang