MediaWiki  REL1_31
Language.php
Go to the documentation of this file.
1 <?php
29 use CLDRPluralRuleParser\Evaluator;
30 
35 class Language {
39  public $mConverter;
40 
41  public $mVariants, $mCode, $mLoaded = false;
42  public $mMagicExtensions = [], $mMagicHookDone = false;
43  private $mHtmlCode = null, $mParentLanguage = false;
44 
45  public $dateFormatStrings = [];
47 
49  protected $namespaceNames;
51 
55  public $transformData = [];
56 
60  static public $dataCache;
61 
62  static public $mLangObjCache = [];
63 
64  static public $mWeekdayMsgs = [
65  'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
66  'friday', 'saturday'
67  ];
68 
69  static public $mWeekdayAbbrevMsgs = [
70  'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
71  ];
72 
73  static public $mMonthMsgs = [
74  'january', 'february', 'march', 'april', 'may_long', 'june',
75  'july', 'august', 'september', 'october', 'november',
76  'december'
77  ];
78  static public $mMonthGenMsgs = [
79  'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
80  'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
81  'december-gen'
82  ];
83  static public $mMonthAbbrevMsgs = [
84  'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
85  'sep', 'oct', 'nov', 'dec'
86  ];
87 
88  static public $mIranianCalendarMonthMsgs = [
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'
93  ];
94 
95  static public $mHebrewCalendarMonthMsgs = [
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'
101  ];
102 
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'
109  ];
110 
111  static public $mHijriCalendarMonthMsgs = [
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'
116  ];
117 
122  static public $durationIntervals = [
123  'millennia' => 31556952000,
124  'centuries' => 3155695200,
125  'decades' => 315569520,
126  'years' => 31556952, // 86400 * ( 365 + ( 24 * 3 + 25 ) / 400 )
127  'weeks' => 604800,
128  'days' => 86400,
129  'hours' => 3600,
130  'minutes' => 60,
131  'seconds' => 1,
132  ];
133 
140  static private $fallbackLanguageCache = [];
141 
146  static private $grammarTransformations;
147 
152  static private $languageNameCache;
153 
157  static private $lre = "\xE2\x80\xAA"; // U+202A LEFT-TO-RIGHT EMBEDDING
158  static private $rle = "\xE2\x80\xAB"; // U+202B RIGHT-TO-LEFT EMBEDDING
159  static private $pdf = "\xE2\x80\xAC"; // U+202C POP DIRECTIONAL FORMATTING
160 
172  // @codeCoverageIgnoreStart
173  // phpcs:ignore Generic.Files.LineLength
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';
175  // @codeCoverageIgnoreEnd
176 
183  static function factory( $code ) {
185 
186  if ( isset( $wgDummyLanguageCodes[$code] ) ) {
188  }
189 
190  // get the language object to process
191  $langObj = isset( self::$mLangObjCache[$code] )
192  ? self::$mLangObjCache[$code]
194 
195  // merge the language object in to get it up front in the cache
196  self::$mLangObjCache = array_merge( [ $code => $langObj ], self::$mLangObjCache );
197  // get rid of the oldest ones in case we have an overflow
198  self::$mLangObjCache = array_slice( self::$mLangObjCache, 0, $wgLangObjCacheSize, true );
199 
200  return $langObj;
201  }
202 
210  protected static function newFromCode( $code, $fallback = false ) {
211  if ( !self::isValidCode( $code ) ) {
212  throw new MWException( "Invalid language code \"$code\"" );
213  }
214 
215  if ( !self::isValidBuiltInCode( $code ) ) {
216  // It's not possible to customise this code with class files, so
217  // just return a Language object. This is to support uselang= hacks.
218  $lang = new Language;
219  $lang->setCode( $code );
220  return $lang;
221  }
222 
223  // Check if there is a language class for the code
224  $class = self::classFromCode( $code, $fallback );
225  if ( class_exists( $class ) ) {
226  $lang = new $class;
227  return $lang;
228  }
229 
230  // Keep trying the fallback list until we find an existing 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'" );
235  }
236 
237  $class = self::classFromCode( $fallbackCode );
238  if ( class_exists( $class ) ) {
239  $lang = new $class;
240  $lang->setCode( $code );
241  return $lang;
242  }
243  }
244 
245  throw new MWException( "Invalid fallback sequence for language '$code'" );
246  }
247 
256  public static function isSupportedLanguage( $code ) {
257  if ( !self::isValidBuiltInCode( $code ) ) {
258  return false;
259  }
260 
261  if ( $code === 'qqq' ) {
262  return false;
263  }
264 
265  return is_readable( self::getMessagesFileName( $code ) ) ||
266  is_readable( self::getJsonMessagesFileName( $code ) );
267  }
268 
284  public static function isWellFormedLanguageTag( $code, $lenient = false ) {
285  $alpha = '[a-z]';
286  $digit = '[0-9]';
287  $alphanum = '[a-z0-9]';
288  $x = 'x'; # private use singleton
289  $singleton = '[a-wy-z]'; # other singleton
290  $s = $lenient ? '[-_]' : '-';
291 
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})+";
298 
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.
303  # https://www.iana.org/assignments/language-subtag-registry
304 
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";
310 
311  $variantList = "$variant(?:$s$variant)*";
312  $extensionList = "$extension(?:$s$extension)*";
313 
314  $langtag = "(?:($language)"
315  . "(?:$s$script)?"
316  . "(?:$s$region)?"
317  . "(?:$s$variantList)?"
318  . "(?:$s$extensionList)?"
319  . "(?:$s$privateUse)?)";
320 
321  # The final breakdown, with capturing groups for each of these components
322  # The variants, extensions, grandfathered, and private-use may have interior '-'
323 
324  $root = "^(?:$langtag|$privateUse|$grandfathered)$";
325 
326  return (bool)preg_match( "/$root/", strtolower( $code ) );
327  }
328 
338  public static function isValidCode( $code ) {
339  static $cache = [];
340  if ( !isset( $cache[$code] ) ) {
341  // People think language codes are html safe, so enforce it.
342  // Ideally we should only allow a-zA-Z0-9-
343  // but, .+ and other chars are often used for {{int:}} hacks
344  // see bugs T39564, T39587, T38938
345  $cache[$code] =
346  // Protect against path traversal
347  strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
349  }
350  return $cache[$code];
351  }
352 
363  public static function isValidBuiltInCode( $code ) {
364  if ( !is_string( $code ) ) {
365  if ( is_object( $code ) ) {
366  $addmsg = " of class " . get_class( $code );
367  } else {
368  $addmsg = '';
369  }
370  $type = gettype( $code );
371  throw new MWException( __METHOD__ . " must be passed a string, $type given$addmsg" );
372  }
373 
374  return (bool)preg_match( '/^[a-z0-9-]{2,}$/', $code );
375  }
376 
385  public static function isKnownLanguageTag( $tag ) {
386  // Quick escape for invalid input to avoid exceptions down the line
387  // when code tries to process tags which are not valid at all.
388  if ( !self::isValidBuiltInCode( $tag ) ) {
389  return false;
390  }
391 
392  if ( isset( MediaWiki\Languages\Data\Names::$names[$tag] )
393  || self::fetchLanguageName( $tag, $tag ) !== ''
394  ) {
395  return true;
396  }
397 
398  return false;
399  }
400 
406  public static function getLocalisationCache() {
407  if ( is_null( self::$dataCache ) ) {
409  $class = $wgLocalisationCacheConf['class'];
410  self::$dataCache = new $class( $wgLocalisationCacheConf );
411  }
412  return self::$dataCache;
413  }
414 
415  function __construct() {
416  $this->mConverter = new FakeConverter( $this );
417  // Set the code to the name of the descendant
418  if ( static::class === 'Language' ) {
419  $this->mCode = 'en';
420  } else {
421  $this->mCode = str_replace( '_', '-', strtolower( substr( static::class, 8 ) ) );
422  }
424  }
425 
429  function __destruct() {
430  foreach ( $this as $name => $value ) {
431  unset( $this->$name );
432  }
433  }
434 
439  function initContLang() {
440  }
441 
446  public function getFallbackLanguages() {
447  return self::getFallbacksFor( $this->mCode );
448  }
449 
454  public function getBookstoreList() {
455  return self::$dataCache->getItem( $this->mCode, 'bookstoreList' );
456  }
457 
464  public function getNamespaces() {
465  if ( is_null( $this->namespaceNames ) ) {
467 
468  $validNamespaces = MWNamespace::getCanonicalNamespaces();
469 
470  $this->namespaceNames = $wgExtraNamespaces +
471  self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
472  $this->namespaceNames += $validNamespaces;
473 
474  $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
475  if ( $wgMetaNamespaceTalk ) {
476  $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
477  } else {
478  $talk = $this->namespaceNames[NS_PROJECT_TALK];
479  $this->namespaceNames[NS_PROJECT_TALK] =
480  $this->fixVariableInNamespace( $talk );
481  }
482 
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] );
487  }
488  }
489 
490  # The above mixing may leave namespaces out of canonical order.
491  # Re-order by namespace ID number...
492  ksort( $this->namespaceNames );
493 
494  Hooks::run( 'LanguageGetNamespaces', [ &$this->namespaceNames ] );
495  }
496 
497  return $this->namespaceNames;
498  }
499 
504  public function setNamespaces( array $namespaces ) {
505  $this->namespaceNames = $namespaces;
506  $this->mNamespaceIds = null;
507  }
508 
512  public function resetNamespaces() {
513  $this->namespaceNames = null;
514  $this->mNamespaceIds = null;
515  $this->namespaceAliases = null;
516  }
517 
524  public function getFormattedNamespaces() {
525  $ns = $this->getNamespaces();
526  foreach ( $ns as $k => $v ) {
527  $ns[$k] = strtr( $v, '_', ' ' );
528  }
529  return $ns;
530  }
531 
543  public function getNsText( $index ) {
544  $ns = $this->getNamespaces();
545  return isset( $ns[$index] ) ? $ns[$index] : false;
546  }
547 
561  public function getFormattedNsText( $index ) {
562  $ns = $this->getNsText( $index );
563  return strtr( $ns, '_', ' ' );
564  }
565 
574  public function getGenderNsText( $index, $gender ) {
576 
578  (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
579 
580  return isset( $ns[$index][$gender] ) ? $ns[$index][$gender] : $this->getNsText( $index );
581  }
582 
589  public function needsGenderDistinction() {
591  if ( count( $wgExtraGenderNamespaces ) > 0 ) {
592  // $wgExtraGenderNamespaces overrides everything
593  return true;
594  } elseif ( isset( $wgExtraNamespaces[NS_USER] ) && isset( $wgExtraNamespaces[NS_USER_TALK] ) ) {
596  // $wgExtraNamespaces overrides any gender aliases specified in i18n files
597  return false;
598  } else {
599  // Check what is in i18n files
600  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
601  return count( $aliases ) > 0;
602  }
603  }
604 
613  function getLocalNsIndex( $text ) {
614  $lctext = $this->lc( $text );
615  $ids = $this->getNamespaceIds();
616  return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
617  }
618 
622  public function getNamespaceAliases() {
623  if ( is_null( $this->namespaceAliases ) ) {
624  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' );
625  if ( !$aliases ) {
626  $aliases = [];
627  } else {
628  foreach ( $aliases as $name => $index ) {
629  if ( $index === NS_PROJECT_TALK ) {
630  unset( $aliases[$name] );
631  $name = $this->fixVariableInNamespace( $name );
632  $aliases[$name] = $index;
633  }
634  }
635  }
636 
638  $genders = $wgExtraGenderNamespaces +
639  (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
640  foreach ( $genders as $index => $forms ) {
641  foreach ( $forms as $alias ) {
642  $aliases[$alias] = $index;
643  }
644  }
645 
646  # Also add converted namespace names as aliases, to avoid confusion.
647  $convertedNames = [];
648  foreach ( $this->getVariants() as $variant ) {
649  if ( $variant === $this->mCode ) {
650  continue;
651  }
652  foreach ( $this->getNamespaces() as $ns => $_ ) {
653  $convertedNames[$this->getConverter()->convertNamespace( $ns, $variant )] = $ns;
654  }
655  }
656 
657  $this->namespaceAliases = $aliases + $convertedNames;
658  }
659 
661  }
662 
666  public function getNamespaceIds() {
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 = [];
674  foreach ( $this->getNamespaces() as $index => $name ) {
675  $this->mNamespaceIds[$this->lc( $name )] = $index;
676  }
677  foreach ( $this->getNamespaceAliases() as $name => $index ) {
678  $this->mNamespaceIds[$this->lc( $name )] = $index;
679  }
680  if ( $wgNamespaceAliases ) {
681  foreach ( $wgNamespaceAliases as $name => $index ) {
682  $this->mNamespaceIds[$this->lc( $name )] = $index;
683  }
684  }
685  }
686  return $this->mNamespaceIds;
687  }
688 
696  public function getNsIndex( $text ) {
697  $lctext = $this->lc( $text );
698  $ns = MWNamespace::getCanonicalIndex( $lctext );
699  if ( $ns !== null ) {
700  return $ns;
701  }
702  $ids = $this->getNamespaceIds();
703  return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
704  }
705 
713  public function getVariantname( $code, $usemsg = true ) {
714  $msg = "variantname-$code";
715  if ( $usemsg && wfMessage( $msg )->exists() ) {
716  return $this->getMessageFromDB( $msg );
717  }
719  if ( $name ) {
720  return $name; # if it's defined as a language name, show that
721  } else {
722  # otherwise, output the language code
723  return $code;
724  }
725  }
726 
730  public function getDatePreferences() {
731  return self::$dataCache->getItem( $this->mCode, 'datePreferences' );
732  }
733 
737  function getDateFormats() {
738  return self::$dataCache->getItem( $this->mCode, 'dateFormats' );
739  }
740 
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';
749  } else {
750  return $df;
751  }
752  }
753 
757  public function getDatePreferenceMigrationMap() {
758  return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' );
759  }
760 
765  function getImageFile( $image ) {
766  return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image );
767  }
768 
773  public function getImageFiles() {
774  return self::$dataCache->getItem( $this->mCode, 'imageFiles' );
775  }
776 
780  public function getExtraUserToggles() {
781  return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles' );
782  }
783 
788  function getUserToggle( $tog ) {
789  return $this->getMessageFromDB( "tog-$tog" );
790  }
791 
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 ] );
808  }
809 
810  $ret = self::$languageNameCache->get( $cacheKey );
811  if ( !$ret ) {
812  $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
813  self::$languageNameCache->set( $cacheKey, $ret );
814  }
815  return $ret;
816  }
817 
828  private static function fetchLanguageNamesUncached( $inLanguage = null, $include = 'mw' ) {
829  global $wgExtraLanguageNames, $wgUsePigLatinVariant;
830 
831  // If passed an invalid language code to use, fallback to en
832  if ( $inLanguage !== null && !self::isValidCode( $inLanguage ) ) {
833  $inLanguage = 'en';
834  }
835 
836  $names = [];
837 
838  if ( $inLanguage ) {
839  # TODO: also include when $inLanguage is null, when this code is more efficient
840  Hooks::run( 'LanguageGetTranslatedLanguageNames', [ &$names, $inLanguage ] );
841  }
842 
843  $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
844  if ( $wgUsePigLatinVariant ) {
845  // Pig Latin (for variant development)
846  $mwNames['en-x-piglatin'] = 'Igpay Atinlay';
847  }
848 
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;
854  }
855  }
856 
857  if ( $include === 'all' ) {
858  ksort( $names );
859  return $names;
860  }
861 
862  $returnMw = [];
863  $coreCodes = array_keys( $mwNames );
864  foreach ( $coreCodes as $coreCode ) {
865  $returnMw[$coreCode] = $names[$coreCode];
866  }
867 
868  if ( $include === 'mwfile' ) {
869  $namesMwFile = [];
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 ) )
875  ) {
876  $namesMwFile[$code] = $names[$code];
877  }
878  }
879 
880  ksort( $namesMwFile );
881  return $namesMwFile;
882  }
883 
884  ksort( $returnMw );
885  # 'mw' option; default if it's not one of the other two options (all/mwfile)
886  return $returnMw;
887  }
888 
896  public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) {
897  $code = strtolower( $code );
898  $array = self::fetchLanguageNames( $inLanguage, $include );
899  return !array_key_exists( $code, $array ) ? '' : $array[$code];
900  }
901 
908  public function getMessageFromDB( $msg ) {
909  return $this->msg( $msg )->text();
910  }
911 
918  protected function msg( $msg ) {
919  return wfMessage( $msg )->inLanguage( $this );
920  }
921 
926  public function getMonthName( $key ) {
927  return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] );
928  }
929 
933  public function getMonthNamesArray() {
934  $monthNames = [ '' ];
935  for ( $i = 1; $i < 13; $i++ ) {
936  $monthNames[] = $this->getMonthName( $i );
937  }
938  return $monthNames;
939  }
940 
945  public function getMonthNameGen( $key ) {
946  return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] );
947  }
948 
953  public function getMonthAbbreviation( $key ) {
954  return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] );
955  }
956 
960  public function getMonthAbbreviationsArray() {
961  $monthNames = [ '' ];
962  for ( $i = 1; $i < 13; $i++ ) {
963  $monthNames[] = $this->getMonthAbbreviation( $i );
964  }
965  return $monthNames;
966  }
967 
972  public function getWeekdayName( $key ) {
973  return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] );
974  }
975 
980  function getWeekdayAbbreviation( $key ) {
981  return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] );
982  }
983 
988  function getIranianCalendarMonthName( $key ) {
989  return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
990  }
991 
996  function getHebrewCalendarMonthName( $key ) {
997  return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
998  }
999 
1005  return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1006  }
1007 
1012  function getHijriCalendarMonthName( $key ) {
1013  return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1014  }
1015 
1024  private static function dateTimeObjFormat( &$dateTimeObj, $ts, $zone, $code ) {
1025  if ( !$dateTimeObj ) {
1026  $dateTimeObj = DateTime::createFromFormat(
1027  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1028  );
1029  }
1030  return $dateTimeObj->format( $code );
1031  }
1032 
1102  public function sprintfDate( $format, $ts, DateTimeZone $zone = null, &$ttl = 'unused' ) {
1103  $s = '';
1104  $raw = false;
1105  $roman = false;
1106  $hebrewNum = false;
1107  $dateTimeObj = false;
1108  $rawToggle = false;
1109  $iranian = false;
1110  $hebrew = false;
1111  $hijri = false;
1112  $thai = false;
1113  $minguo = false;
1114  $tenno = false;
1115 
1116  $usedSecond = false;
1117  $usedMinute = false;
1118  $usedHour = false;
1119  $usedAMPM = false;
1120  $usedDay = false;
1121  $usedWeek = false;
1122  $usedMonth = false;
1123  $usedYear = false;
1124  $usedISOYear = false;
1125  $usedIsLeapYear = false;
1126 
1127  $usedHebrewMonth = false;
1128  $usedIranianMonth = false;
1129  $usedHijriMonth = false;
1130  $usedHebrewYear = false;
1131  $usedIranianYear = false;
1132  $usedHijriYear = false;
1133  $usedTennoYear = false;
1134 
1135  if ( strlen( $ts ) !== 14 ) {
1136  throw new MWException( __METHOD__ . ": The timestamp $ts should have 14 characters" );
1137  }
1138 
1139  if ( !ctype_digit( $ts ) ) {
1140  throw new MWException( __METHOD__ . ": The timestamp $ts should be a number" );
1141  }
1142 
1143  $formatLength = strlen( $format );
1144  for ( $p = 0; $p < $formatLength; $p++ ) {
1145  $num = false;
1146  $code = $format[$p];
1147  if ( $code == 'x' && $p < $formatLength - 1 ) {
1148  $code .= $format[++$p];
1149  }
1150 
1151  if ( ( $code === 'xi'
1152  || $code === 'xj'
1153  || $code === 'xk'
1154  || $code === 'xm'
1155  || $code === 'xo'
1156  || $code === 'xt' )
1157  && $p < $formatLength - 1 ) {
1158  $code .= $format[++$p];
1159  }
1160 
1161  switch ( $code ) {
1162  case 'xx':
1163  $s .= 'x';
1164  break;
1165  case 'xn':
1166  $raw = true;
1167  break;
1168  case 'xN':
1169  $rawToggle = !$rawToggle;
1170  break;
1171  case 'xr':
1172  $roman = true;
1173  break;
1174  case 'xh':
1175  $hebrewNum = true;
1176  break;
1177  case 'xg':
1178  $usedMonth = true;
1179  $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
1180  break;
1181  case 'xjx':
1182  $usedHebrewMonth = true;
1183  if ( !$hebrew ) {
1184  $hebrew = self::tsToHebrew( $ts );
1185  }
1186  $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
1187  break;
1188  case 'd':
1189  $usedDay = true;
1190  $num = substr( $ts, 6, 2 );
1191  break;
1192  case 'D':
1193  $usedDay = true;
1194  $s .= $this->getWeekdayAbbreviation(
1195  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
1196  );
1197  break;
1198  case 'j':
1199  $usedDay = true;
1200  $num = intval( substr( $ts, 6, 2 ) );
1201  break;
1202  case 'xij':
1203  $usedDay = true;
1204  if ( !$iranian ) {
1205  $iranian = self::tsToIranian( $ts );
1206  }
1207  $num = $iranian[2];
1208  break;
1209  case 'xmj':
1210  $usedDay = true;
1211  if ( !$hijri ) {
1212  $hijri = self::tsToHijri( $ts );
1213  }
1214  $num = $hijri[2];
1215  break;
1216  case 'xjj':
1217  $usedDay = true;
1218  if ( !$hebrew ) {
1219  $hebrew = self::tsToHebrew( $ts );
1220  }
1221  $num = $hebrew[2];
1222  break;
1223  case 'l':
1224  $usedDay = true;
1225  $s .= $this->getWeekdayName(
1226  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
1227  );
1228  break;
1229  case 'F':
1230  $usedMonth = true;
1231  $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
1232  break;
1233  case 'xiF':
1234  $usedIranianMonth = true;
1235  if ( !$iranian ) {
1236  $iranian = self::tsToIranian( $ts );
1237  }
1238  $s .= $this->getIranianCalendarMonthName( $iranian[1] );
1239  break;
1240  case 'xmF':
1241  $usedHijriMonth = true;
1242  if ( !$hijri ) {
1243  $hijri = self::tsToHijri( $ts );
1244  }
1245  $s .= $this->getHijriCalendarMonthName( $hijri[1] );
1246  break;
1247  case 'xjF':
1248  $usedHebrewMonth = true;
1249  if ( !$hebrew ) {
1250  $hebrew = self::tsToHebrew( $ts );
1251  }
1252  $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
1253  break;
1254  case 'm':
1255  $usedMonth = true;
1256  $num = substr( $ts, 4, 2 );
1257  break;
1258  case 'M':
1259  $usedMonth = true;
1260  $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
1261  break;
1262  case 'n':
1263  $usedMonth = true;
1264  $num = intval( substr( $ts, 4, 2 ) );
1265  break;
1266  case 'xin':
1267  $usedIranianMonth = true;
1268  if ( !$iranian ) {
1269  $iranian = self::tsToIranian( $ts );
1270  }
1271  $num = $iranian[1];
1272  break;
1273  case 'xmn':
1274  $usedHijriMonth = true;
1275  if ( !$hijri ) {
1276  $hijri = self::tsToHijri( $ts );
1277  }
1278  $num = $hijri[1];
1279  break;
1280  case 'xjn':
1281  $usedHebrewMonth = true;
1282  if ( !$hebrew ) {
1283  $hebrew = self::tsToHebrew( $ts );
1284  }
1285  $num = $hebrew[1];
1286  break;
1287  case 'xjt':
1288  $usedHebrewMonth = true;
1289  if ( !$hebrew ) {
1290  $hebrew = self::tsToHebrew( $ts );
1291  }
1292  $num = $hebrew[3];
1293  break;
1294  case 'Y':
1295  $usedYear = true;
1296  $num = substr( $ts, 0, 4 );
1297  break;
1298  case 'xiY':
1299  $usedIranianYear = true;
1300  if ( !$iranian ) {
1301  $iranian = self::tsToIranian( $ts );
1302  }
1303  $num = $iranian[0];
1304  break;
1305  case 'xmY':
1306  $usedHijriYear = true;
1307  if ( !$hijri ) {
1308  $hijri = self::tsToHijri( $ts );
1309  }
1310  $num = $hijri[0];
1311  break;
1312  case 'xjY':
1313  $usedHebrewYear = true;
1314  if ( !$hebrew ) {
1315  $hebrew = self::tsToHebrew( $ts );
1316  }
1317  $num = $hebrew[0];
1318  break;
1319  case 'xkY':
1320  $usedYear = true;
1321  if ( !$thai ) {
1322  $thai = self::tsToYear( $ts, 'thai' );
1323  }
1324  $num = $thai[0];
1325  break;
1326  case 'xoY':
1327  $usedYear = true;
1328  if ( !$minguo ) {
1329  $minguo = self::tsToYear( $ts, 'minguo' );
1330  }
1331  $num = $minguo[0];
1332  break;
1333  case 'xtY':
1334  $usedTennoYear = true;
1335  if ( !$tenno ) {
1336  $tenno = self::tsToYear( $ts, 'tenno' );
1337  }
1338  $num = $tenno[0];
1339  break;
1340  case 'y':
1341  $usedYear = true;
1342  $num = substr( $ts, 2, 2 );
1343  break;
1344  case 'xiy':
1345  $usedIranianYear = true;
1346  if ( !$iranian ) {
1347  $iranian = self::tsToIranian( $ts );
1348  }
1349  $num = substr( $iranian[0], -2 );
1350  break;
1351  case 'xit':
1352  $usedIranianYear = true;
1353  if ( !$iranian ) {
1354  $iranian = self::tsToIranian( $ts );
1355  }
1356  $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1357  break;
1358  case 'xiz':
1359  $usedIranianYear = true;
1360  if ( !$iranian ) {
1361  $iranian = self::tsToIranian( $ts );
1362  }
1363  $num = $iranian[3];
1364  break;
1365  case 'a':
1366  $usedAMPM = true;
1367  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
1368  break;
1369  case 'A':
1370  $usedAMPM = true;
1371  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
1372  break;
1373  case 'g':
1374  $usedHour = true;
1375  $h = substr( $ts, 8, 2 );
1376  $num = $h % 12 ? $h % 12 : 12;
1377  break;
1378  case 'G':
1379  $usedHour = true;
1380  $num = intval( substr( $ts, 8, 2 ) );
1381  break;
1382  case 'h':
1383  $usedHour = true;
1384  $h = substr( $ts, 8, 2 );
1385  $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
1386  break;
1387  case 'H':
1388  $usedHour = true;
1389  $num = substr( $ts, 8, 2 );
1390  break;
1391  case 'i':
1392  $usedMinute = true;
1393  $num = substr( $ts, 10, 2 );
1394  break;
1395  case 's':
1396  $usedSecond = true;
1397  $num = substr( $ts, 12, 2 );
1398  break;
1399  case 'c':
1400  case 'r':
1401  $usedSecond = true;
1402  // fall through
1403  case 'e':
1404  case 'O':
1405  case 'P':
1406  case 'T':
1407  $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1408  break;
1409  case 'w':
1410  case 'N':
1411  case 'z':
1412  $usedDay = true;
1413  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1414  break;
1415  case 'W':
1416  $usedWeek = true;
1417  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1418  break;
1419  case 't':
1420  $usedMonth = true;
1421  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1422  break;
1423  case 'L':
1424  $usedIsLeapYear = true;
1425  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1426  break;
1427  case 'o':
1428  $usedISOYear = true;
1429  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1430  break;
1431  case 'U':
1432  $usedSecond = true;
1433  // fall through
1434  case 'I':
1435  case 'Z':
1436  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1437  break;
1438  case '\\':
1439  # Backslash escaping
1440  if ( $p < $formatLength - 1 ) {
1441  $s .= $format[++$p];
1442  } else {
1443  $s .= '\\';
1444  }
1445  break;
1446  case '"':
1447  # Quoted literal
1448  if ( $p < $formatLength - 1 ) {
1449  $endQuote = strpos( $format, '"', $p + 1 );
1450  if ( $endQuote === false ) {
1451  # No terminating quote, assume literal "
1452  $s .= '"';
1453  } else {
1454  $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1455  $p = $endQuote;
1456  }
1457  } else {
1458  # Quote at end of string, assume literal "
1459  $s .= '"';
1460  }
1461  break;
1462  default:
1463  $s .= $format[$p];
1464  }
1465  if ( $num !== false ) {
1466  if ( $rawToggle || $raw ) {
1467  $s .= $num;
1468  $raw = false;
1469  } elseif ( $roman ) {
1470  $s .= self::romanNumeral( $num );
1471  $roman = false;
1472  } elseif ( $hebrewNum ) {
1473  $s .= self::hebrewNumeral( $num );
1474  $hebrewNum = false;
1475  } else {
1476  $s .= $this->formatNum( $num, true );
1477  }
1478  }
1479  }
1480 
1481  if ( $ttl === 'unused' ) {
1482  // No need to calculate the TTL, the caller wont use it anyway.
1483  } elseif ( $usedSecond ) {
1484  $ttl = 1;
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 );
1492  } elseif (
1493  $usedDay ||
1494  $usedHebrewMonth ||
1495  $usedIranianMonth ||
1496  $usedHijriMonth ||
1497  $usedHebrewYear ||
1498  $usedIranianYear ||
1499  $usedHijriYear ||
1500  $usedTennoYear
1501  ) {
1502  // @todo Someone who understands the non-Gregorian calendars
1503  // should write proper logic for them so that they don't need purged every day.
1504  $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1505  substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1506  } else {
1507  $possibleTtls = [];
1508  $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1509  substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1510  if ( $usedWeek ) {
1511  $possibleTtls[] =
1512  ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
1513  $timeRemainingInDay;
1514  } elseif ( $usedISOYear ) {
1515  // December 28th falls on the last ISO week of the year, every year.
1516  // The last ISO week of a year can be 52 or 53.
1517  $lastWeekOfISOYear = DateTime::createFromFormat(
1518  'Ymd',
1519  substr( $ts, 0, 4 ) . '1228',
1520  $zone ?: new DateTimeZone( 'UTC' )
1521  )->format( 'W' );
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;
1528  }
1529 
1530  if ( $usedMonth ) {
1531  $possibleTtls[] =
1532  ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
1533  substr( $ts, 6, 2 ) ) * 86400
1534  + $timeRemainingInDay;
1535  } elseif ( $usedYear ) {
1536  $possibleTtls[] =
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;
1546  $mod = $year % 4;
1547  if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1548  // this isn't a leap year. see when the next one starts
1549  $nextCandidate = $year - $mod + 4;
1550  if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1551  $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1552  $timeRemainingInYear;
1553  } else {
1554  $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1555  $timeRemainingInYear;
1556  }
1557  } else {
1558  // this is a leap year, so the next year isn't
1559  $possibleTtls[] = $timeRemainingInYear;
1560  }
1561  }
1562 
1563  if ( $possibleTtls ) {
1564  $ttl = min( $possibleTtls );
1565  }
1566  }
1567 
1568  return $s;
1569  }
1570 
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 ];
1573 
1586  private static function tsToIranian( $ts ) {
1587  $gy = substr( $ts, 0, 4 ) - 1600;
1588  $gm = substr( $ts, 4, 2 ) - 1;
1589  $gd = substr( $ts, 6, 2 ) - 1;
1590 
1591  # Days passed from the beginning (including leap years)
1592  $gDayNo = 365 * $gy
1593  + floor( ( $gy + 3 ) / 4 )
1594  - floor( ( $gy + 99 ) / 100 )
1595  + floor( ( $gy + 399 ) / 400 );
1596 
1597  // Add days of the past months of this year
1598  for ( $i = 0; $i < $gm; $i++ ) {
1599  $gDayNo += self::$GREG_DAYS[$i];
1600  }
1601 
1602  // Leap years
1603  if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1604  $gDayNo++;
1605  }
1606 
1607  // Days passed in current month
1608  $gDayNo += (int)$gd;
1609 
1610  $jDayNo = $gDayNo - 79;
1611 
1612  $jNp = floor( $jDayNo / 12053 );
1613  $jDayNo %= 12053;
1614 
1615  $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1616  $jDayNo %= 1461;
1617 
1618  if ( $jDayNo >= 366 ) {
1619  $jy += floor( ( $jDayNo - 1 ) / 365 );
1620  $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1621  }
1622 
1623  $jz = $jDayNo;
1624 
1625  for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1626  $jDayNo -= self::$IRANIAN_DAYS[$i];
1627  }
1628 
1629  $jm = $i + 1;
1630  $jd = $jDayNo + 1;
1631 
1632  return [ $jy, $jm, $jd, $jz ];
1633  }
1634 
1646  private static function tsToHijri( $ts ) {
1647  $year = substr( $ts, 0, 4 );
1648  $month = substr( $ts, 4, 2 );
1649  $day = substr( $ts, 6, 2 );
1650 
1651  $zyr = $year;
1652  $zd = $day;
1653  $zm = $month;
1654  $zy = $zyr;
1655 
1656  if (
1657  ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1658  ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1659  ) {
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 ) +
1663  $zd - 32075;
1664  } else {
1665  $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1666  (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1667  }
1668 
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;
1679 
1680  return [ $zy, $zm, $zd ];
1681  }
1682 
1698  private static function tsToHebrew( $ts ) {
1699  # Parse date
1700  $year = substr( $ts, 0, 4 );
1701  $month = substr( $ts, 4, 2 );
1702  $day = substr( $ts, 6, 2 );
1703 
1704  # Calculate Hebrew year
1705  $hebrewYear = $year + 3760;
1706 
1707  # Month number when September = 1, August = 12
1708  $month += 4;
1709  if ( $month > 12 ) {
1710  # Next year
1711  $month -= 12;
1712  $year++;
1713  $hebrewYear++;
1714  }
1715 
1716  # Calculate day of year from 1 September
1717  $dayOfYear = $day;
1718  for ( $i = 1; $i < $month; $i++ ) {
1719  if ( $i == 6 ) {
1720  # February
1721  $dayOfYear += 28;
1722  # Check if the year is leap
1723  if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1724  $dayOfYear++;
1725  }
1726  } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1727  $dayOfYear += 30;
1728  } else {
1729  $dayOfYear += 31;
1730  }
1731  }
1732 
1733  # Calculate the start of the Hebrew year
1734  $start = self::hebrewYearStart( $hebrewYear );
1735 
1736  # Calculate next year's start
1737  if ( $dayOfYear <= $start ) {
1738  # Day is before the start of the year - it is the previous year
1739  # Next year's start
1740  $nextStart = $start;
1741  # Previous year
1742  $year--;
1743  $hebrewYear--;
1744  # Add days since previous year's 1 September
1745  $dayOfYear += 365;
1746  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1747  # Leap year
1748  $dayOfYear++;
1749  }
1750  # Start of the new (previous) year
1751  $start = self::hebrewYearStart( $hebrewYear );
1752  } else {
1753  # Next year's start
1754  $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1755  }
1756 
1757  # Calculate Hebrew day of year
1758  $hebrewDayOfYear = $dayOfYear - $start;
1759 
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 ) ) {
1766  $diff += 13;
1767  } else {
1768  $diff += 12;
1769  }
1770 
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;
1778 
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;
1783  $hebrewMonth = 1;
1784  $days = 0;
1785  while ( $hebrewMonth <= 12 ) {
1786  # Calculate days in this month
1787  if ( $isLeap && $hebrewMonth == 6 ) {
1788  # Adar in a leap year
1789  if ( $isLeap ) {
1790  # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1791  $days = 30;
1792  if ( $hebrewDay <= $days ) {
1793  # Day in Adar I
1794  $hebrewMonth = 13;
1795  } else {
1796  # Subtract the days of Adar I
1797  $hebrewDay -= $days;
1798  # Try Adar II
1799  $days = 29;
1800  if ( $hebrewDay <= $days ) {
1801  # Day in Adar II
1802  $hebrewMonth = 14;
1803  }
1804  }
1805  }
1806  } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1807  # Cheshvan in a complete year (otherwise as the rule below)
1808  $days = 30;
1809  } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1810  # Kislev in an incomplete year (otherwise as the rule below)
1811  $days = 29;
1812  } else {
1813  # Odd months have 30 days, even have 29
1814  $days = 30 - ( $hebrewMonth - 1 ) % 2;
1815  }
1816  if ( $hebrewDay <= $days ) {
1817  # In the current month
1818  break;
1819  } else {
1820  # Subtract the days of the current month
1821  $hebrewDay -= $days;
1822  # Try in the next month
1823  $hebrewMonth++;
1824  }
1825  }
1826 
1827  return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1828  }
1829 
1839  private static function hebrewYearStart( $year ) {
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 );
1843  if ( $m < 0 ) {
1844  $m--;
1845  }
1846  $Mar = intval( $m );
1847  if ( $m < 0 ) {
1848  $m++;
1849  }
1850  $m -= $Mar;
1851 
1852  $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1853  if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1854  $Mar++;
1855  } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1856  $Mar += 2;
1857  } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1858  $Mar++;
1859  }
1860 
1861  $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1862  return $Mar;
1863  }
1864 
1877  private static function tsToYear( $ts, $cName ) {
1878  $gy = substr( $ts, 0, 4 );
1879  $gm = substr( $ts, 4, 2 );
1880  $gd = substr( $ts, 6, 2 );
1881 
1882  if ( !strcmp( $cName, 'thai' ) ) {
1883  # Thai solar dates
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' ) ) {
1888  # Minguo dates
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
1897  if ( ( $gy < 1912 )
1898  || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1899  || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1900  ) {
1901  # Meiji period
1902  $gy_gannen = $gy - 1868 + 1;
1903  $gy_offset = $gy_gannen;
1904  if ( $gy_gannen == 1 ) {
1905  $gy_offset = '元';
1906  }
1907  $gy_offset = '明治' . $gy_offset;
1908  } elseif (
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 ) )
1914  ) {
1915  # Taishō period
1916  $gy_gannen = $gy - 1912 + 1;
1917  $gy_offset = $gy_gannen;
1918  if ( $gy_gannen == 1 ) {
1919  $gy_offset = '元';
1920  }
1921  $gy_offset = '大正' . $gy_offset;
1922  } elseif (
1923  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1924  ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1925  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1926  ) {
1927  # Shōwa period
1928  $gy_gannen = $gy - 1926 + 1;
1929  $gy_offset = $gy_gannen;
1930  if ( $gy_gannen == 1 ) {
1931  $gy_offset = '元';
1932  }
1933  $gy_offset = '昭和' . $gy_offset;
1934  } elseif (
1935  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd >= 8 ) ) ||
1936  ( ( $gy > 1989 ) && ( $gy < 2019 ) ) ||
1937  ( ( $gy == 2019 ) && ( $gm < 5 ) )
1938  ) {
1939  # Heisei period
1940  $gy_gannen = $gy - 1989 + 1;
1941  $gy_offset = $gy_gannen;
1942  if ( $gy_gannen == 1 ) {
1943  $gy_offset = '元';
1944  }
1945  $gy_offset = '平成' . $gy_offset;
1946  } else {
1947  # Reiwa period
1948  $gy_gannen = $gy - 2019 + 1;
1949  $gy_offset = $gy_gannen;
1950  if ( $gy_gannen == 1 ) {
1951  $gy_offset = '元';
1952  }
1953  $gy_offset = '令和' . $gy_offset;
1954  }
1955  } else {
1956  $gy_offset = $gy;
1957  }
1958 
1959  return [ $gy_offset, $gm, $gd ];
1960  }
1961 
1975  private static function strongDirFromContent( $text = '' ) {
1976  if ( !preg_match( self::$strongDirRegex, $text, $matches ) ) {
1977  return null;
1978  }
1979  if ( $matches[1] === '' ) {
1980  return 'rtl';
1981  }
1982  return 'ltr';
1983  }
1984 
1992  static function romanNumeral( $num ) {
1993  static $table = [
1994  [ '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ],
1995  [ '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ],
1996  [ '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ],
1997  [ '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM',
1998  'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' ]
1999  ];
2000 
2001  $num = intval( $num );
2002  if ( $num > 10000 || $num <= 0 ) {
2003  return $num;
2004  }
2005 
2006  $s = '';
2007  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2008  if ( $num >= $pow10 ) {
2009  $s .= $table[$i][(int)floor( $num / $pow10 )];
2010  }
2011  $num = $num % $pow10;
2012  }
2013  return $s;
2014  }
2015 
2023  static function hebrewNumeral( $num ) {
2024  static $table = [
2025  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ],
2026  [ '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ],
2027  [ '',
2028  [ 'ק' ],
2029  [ 'ר' ],
2030  [ 'ש' ],
2031  [ 'ת' ],
2032  [ 'ת', 'ק' ],
2033  [ 'ת', 'ר' ],
2034  [ 'ת', 'ש' ],
2035  [ 'ת', 'ת' ],
2036  [ 'ת', 'ת', 'ק' ],
2037  [ 'ת', 'ת', 'ר' ],
2038  ],
2039  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ]
2040  ];
2041 
2042  $num = intval( $num );
2043  if ( $num > 9999 || $num <= 0 ) {
2044  return $num;
2045  }
2046 
2047  // Round thousands have special notations
2048  if ( $num === 1000 ) {
2049  return "א' אלף";
2050  } elseif ( $num % 1000 === 0 ) {
2051  return $table[0][$num / 1000] . "' אלפים";
2052  }
2053 
2054  $letters = [];
2055 
2056  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2057  if ( $num >= $pow10 ) {
2058  if ( $num === 15 || $num === 16 ) {
2059  $letters[] = $table[0][9];
2060  $letters[] = $table[0][$num - 9];
2061  $num = 0;
2062  } else {
2063  $letters = array_merge(
2064  $letters,
2065  (array)$table[$i][intval( $num / $pow10 )]
2066  );
2067 
2068  if ( $pow10 === 1000 ) {
2069  $letters[] = "'";
2070  }
2071  }
2072  }
2073 
2074  $num = $num % $pow10;
2075  }
2076 
2077  $preTransformLength = count( $letters );
2078  if ( $preTransformLength === 1 ) {
2079  // Add geresh (single quote) to one-letter numbers
2080  $letters[] = "'";
2081  } else {
2082  $lastIndex = $preTransformLength - 1;
2083  $letters[$lastIndex] = str_replace(
2084  [ 'כ', 'מ', 'נ', 'פ', 'צ' ],
2085  [ 'ך', 'ם', 'ן', 'ף', 'ץ' ],
2086  $letters[$lastIndex]
2087  );
2088 
2089  // Add gershayim (double quote) to multiple-letter numbers,
2090  // but exclude numbers with only one letter after the thousands
2091  // (1001-1009, 1020, 1030, 2001-2009, etc.)
2092  if ( $letters[1] === "'" && $preTransformLength === 3 ) {
2093  $letters[] = "'";
2094  } else {
2095  array_splice( $letters, -1, 0, '"' );
2096  }
2097  }
2098 
2099  return implode( $letters );
2100  }
2101 
2110  public function userAdjust( $ts, $tz = false ) {
2112 
2113  if ( $tz === false ) {
2114  $tz = $wgUser->getOption( 'timecorrection' );
2115  }
2116 
2117  $data = explode( '|', $tz, 3 );
2118 
2119  if ( $data[0] == 'ZoneInfo' ) {
2120  try {
2121  $userTZ = new DateTimeZone( $data[2] );
2122  $date = new DateTime( $ts, new DateTimeZone( 'UTC' ) );
2123  $date->setTimezone( $userTZ );
2124  return $date->format( 'YmdHis' );
2125  } catch ( Exception $e ) {
2126  // Unrecognized timezone, default to 'Offset' with the stored offset.
2127  $data[0] = 'Offset';
2128  }
2129  }
2130 
2131  if ( $data[0] == 'System' || $tz == '' ) {
2132  # Global offset in minutes.
2133  $minDiff = $wgLocalTZoffset;
2134  } elseif ( $data[0] == 'Offset' ) {
2135  $minDiff = intval( $data[1] );
2136  } else {
2137  $data = explode( ':', $tz );
2138  if ( count( $data ) == 2 ) {
2139  $data[0] = intval( $data[0] );
2140  $data[1] = intval( $data[1] );
2141  $minDiff = abs( $data[0] ) * 60 + $data[1];
2142  if ( $data[0] < 0 ) {
2143  $minDiff = -$minDiff;
2144  }
2145  } else {
2146  $minDiff = intval( $data[0] ) * 60;
2147  }
2148  }
2149 
2150  # No difference ? Return time unchanged
2151  if ( 0 == $minDiff ) {
2152  return $ts;
2153  }
2154 
2155  Wikimedia\suppressWarnings(); // E_STRICT system time bitching
2156  # Generate an adjusted date; take advantage of the fact that mktime
2157  # will normalize out-of-range values so we don't have to split $minDiff
2158  # into hours and minutes.
2159  $t = mktime( (
2160  (int)substr( $ts, 8, 2 ) ), # Hours
2161  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2162  (int)substr( $ts, 12, 2 ), # Seconds
2163  (int)substr( $ts, 4, 2 ), # Month
2164  (int)substr( $ts, 6, 2 ), # Day
2165  (int)substr( $ts, 0, 4 ) ); # Year
2166 
2167  $date = date( 'YmdHis', $t );
2168  Wikimedia\restoreWarnings();
2169 
2170  return $date;
2171  }
2172 
2188  function dateFormat( $usePrefs = true ) {
2189  global $wgUser;
2190 
2191  if ( is_bool( $usePrefs ) ) {
2192  if ( $usePrefs ) {
2193  $datePreference = $wgUser->getDatePreference();
2194  } else {
2195  $datePreference = (string)User::getDefaultOption( 'date' );
2196  }
2197  } else {
2198  $datePreference = (string)$usePrefs;
2199  }
2200 
2201  // return int
2202  if ( $datePreference == '' ) {
2203  return 'default';
2204  }
2205 
2206  return $datePreference;
2207  }
2208 
2219  function getDateFormatString( $type, $pref ) {
2220  $wasDefault = false;
2221  if ( $pref == 'default' ) {
2222  $wasDefault = true;
2223  $pref = $this->getDefaultDateFormat();
2224  }
2225 
2226  if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
2227  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2228 
2229  if ( $type === 'pretty' && $df === null ) {
2230  $df = $this->getDateFormatString( 'date', $pref );
2231  }
2232 
2233  if ( !$wasDefault && $df === null ) {
2234  $pref = $this->getDefaultDateFormat();
2235  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2236  }
2237 
2238  $this->dateFormatStrings[$type][$pref] = $df;
2239  }
2240  return $this->dateFormatStrings[$type][$pref];
2241  }
2242 
2253  public function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
2254  $ts = wfTimestamp( TS_MW, $ts );
2255  if ( $adj ) {
2256  $ts = $this->userAdjust( $ts, $timecorrection );
2257  }
2258  $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
2259  return $this->sprintfDate( $df, $ts );
2260  }
2261 
2272  public function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
2273  $ts = wfTimestamp( TS_MW, $ts );
2274  if ( $adj ) {
2275  $ts = $this->userAdjust( $ts, $timecorrection );
2276  }
2277  $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
2278  return $this->sprintfDate( $df, $ts );
2279  }
2280 
2292  public function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
2293  $ts = wfTimestamp( TS_MW, $ts );
2294  if ( $adj ) {
2295  $ts = $this->userAdjust( $ts, $timecorrection );
2296  }
2297  $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
2298  return $this->sprintfDate( $df, $ts );
2299  }
2300 
2311  public function formatDuration( $seconds, array $chosenIntervals = [] ) {
2312  $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals );
2313 
2314  $segments = [];
2315 
2316  foreach ( $intervals as $intervalName => $intervalValue ) {
2317  // Messages: duration-seconds, duration-minutes, duration-hours, duration-days, duration-weeks,
2318  // duration-years, duration-decades, duration-centuries, duration-millennia
2319  $message = wfMessage( 'duration-' . $intervalName )->numParams( $intervalValue );
2320  $segments[] = $message->inLanguage( $this )->escaped();
2321  }
2322 
2323  return $this->listToText( $segments );
2324  }
2325 
2337  public function getDurationIntervals( $seconds, array $chosenIntervals = [] ) {
2338  if ( empty( $chosenIntervals ) ) {
2339  $chosenIntervals = [
2340  'millennia',
2341  'centuries',
2342  'decades',
2343  'years',
2344  'days',
2345  'hours',
2346  'minutes',
2347  'seconds'
2348  ];
2349  }
2350 
2351  $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2352  $sortedNames = array_keys( $intervals );
2353  $smallestInterval = array_pop( $sortedNames );
2354 
2355  $segments = [];
2356 
2357  foreach ( $intervals as $name => $length ) {
2358  $value = floor( $seconds / $length );
2359 
2360  if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2361  $seconds -= $value * $length;
2362  $segments[$name] = $value;
2363  }
2364  }
2365 
2366  return $segments;
2367  }
2368 
2388  private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
2389  $ts = wfTimestamp( TS_MW, $ts );
2390  $options += [ 'timecorrection' => true, 'format' => true ];
2391  if ( $options['timecorrection'] !== false ) {
2392  if ( $options['timecorrection'] === true ) {
2393  $offset = $user->getOption( 'timecorrection' );
2394  } else {
2395  $offset = $options['timecorrection'];
2396  }
2397  $ts = $this->userAdjust( $ts, $offset );
2398  }
2399  if ( $options['format'] === true ) {
2400  $format = $user->getDatePreference();
2401  } else {
2402  $format = $options['format'];
2403  }
2404  $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) );
2405  return $this->sprintfDate( $df, $ts );
2406  }
2407 
2427  public function userDate( $ts, User $user, array $options = [] ) {
2428  return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
2429  }
2430 
2450  public function userTime( $ts, User $user, array $options = [] ) {
2451  return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
2452  }
2453 
2473  public function userTimeAndDate( $ts, User $user, array $options = [] ) {
2474  return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
2475  }
2476 
2492  public function getHumanTimestamp(
2493  MWTimestamp $time, MWTimestamp $relativeTo = null, User $user = null
2494  ) {
2495  if ( $relativeTo === null ) {
2496  $relativeTo = new MWTimestamp();
2497  }
2498  if ( $user === null ) {
2499  $user = RequestContext::getMain()->getUser();
2500  }
2501 
2502  // Adjust for the user's timezone.
2503  $offsetThis = $time->offsetForUser( $user );
2504  $offsetRel = $relativeTo->offsetForUser( $user );
2505 
2506  $ts = '';
2507  if ( Hooks::run( 'GetHumanTimestamp', [ &$ts, $time, $relativeTo, $user, $this ] ) ) {
2508  $ts = $this->getHumanTimestampInternal( $time, $relativeTo, $user );
2509  }
2510 
2511  // Reset the timezone on the objects.
2512  $time->timestamp->sub( $offsetThis );
2513  $relativeTo->timestamp->sub( $offsetRel );
2514 
2515  return $ts;
2516  }
2517 
2529  private function getHumanTimestampInternal(
2530  MWTimestamp $ts, MWTimestamp $relativeTo, User $user
2531  ) {
2532  $diff = $ts->diff( $relativeTo );
2533  $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) -
2534  (int)$relativeTo->timestamp->format( 'w' ) );
2535  $days = $diff->days ?: (int)$diffDay;
2536  if ( $diff->invert || $days > 5
2537  && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' )
2538  ) {
2539  // Timestamps are in different years: use full timestamp
2540  // Also do full timestamp for future dates
2544  $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
2545  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2546  } elseif ( $days > 5 ) {
2547  // Timestamps are in same year, but more than 5 days ago: show day and month only.
2548  $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
2549  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2550  } elseif ( $days > 1 ) {
2551  // Timestamp within the past week: show the day of the week and time
2552  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2553  $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
2554  // Messages:
2555  // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
2556  $ts = wfMessage( "$weekday-at" )
2557  ->inLanguage( $this )
2558  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2559  ->text();
2560  } elseif ( $days == 1 ) {
2561  // Timestamp was yesterday: say 'yesterday' and the time.
2562  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2563  $ts = wfMessage( 'yesterday-at' )
2564  ->inLanguage( $this )
2565  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2566  ->text();
2567  } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2568  // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
2569  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2570  $ts = wfMessage( 'today-at' )
2571  ->inLanguage( $this )
2572  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2573  ->text();
2574 
2575  // From here on in, the timestamp was soon enough ago so that we can simply say
2576  // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
2577  } elseif ( $diff->h == 1 ) {
2578  // Less than 90 minutes, but more than an hour ago.
2579  $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2580  } elseif ( $diff->i >= 1 ) {
2581  // A few minutes ago.
2582  $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2583  } elseif ( $diff->s >= 30 ) {
2584  // Less than a minute, but more than 30 sec ago.
2585  $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2586  } else {
2587  // Less than 30 seconds ago.
2588  $ts = wfMessage( 'just-now' )->text();
2589  }
2590 
2591  return $ts;
2592  }
2593 
2598  public function getMessage( $key ) {
2599  return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
2600  }
2601 
2605  function getAllMessages() {
2606  return self::$dataCache->getItem( $this->mCode, 'messages' );
2607  }
2608 
2615  public function iconv( $in, $out, $string ) {
2616  # Even with //IGNORE iconv can whine about illegal characters in
2617  # *input* string. We just ignore those too.
2618  # REF: https://bugs.php.net/bug.php?id=37166
2619  # REF: https://phabricator.wikimedia.org/T18885
2620  Wikimedia\suppressWarnings();
2621  $text = iconv( $in, $out . '//IGNORE', $string );
2622  Wikimedia\restoreWarnings();
2623  return $text;
2624  }
2625 
2626  // callback functions for ucwords(), ucwordbreaks()
2627 
2633  return $this->ucfirst( $matches[1] );
2634  }
2635 
2641  return mb_strtoupper( $matches[0] );
2642  }
2643 
2649  return mb_strtoupper( $matches[0] );
2650  }
2651 
2659  public function ucfirst( $str ) {
2660  $o = ord( $str );
2661  if ( $o < 96 ) { // if already uppercase...
2662  return $str;
2663  } elseif ( $o < 128 ) {
2664  return ucfirst( $str ); // use PHP's ucfirst()
2665  } else {
2666  // fall back to more complex logic in case of multibyte strings
2667  return $this->uc( $str, true );
2668  }
2669  }
2670 
2679  public function uc( $str, $first = false ) {
2680  if ( $first ) {
2681  if ( $this->isMultibyte( $str ) ) {
2682  return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2683  } else {
2684  return ucfirst( $str );
2685  }
2686  } else {
2687  return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2688  }
2689  }
2690 
2695  function lcfirst( $str ) {
2696  $o = ord( $str );
2697  if ( !$o ) {
2698  return strval( $str );
2699  } elseif ( $o >= 128 ) {
2700  return $this->lc( $str, true );
2701  } elseif ( $o > 96 ) {
2702  return $str;
2703  } else {
2704  $str[0] = strtolower( $str[0] );
2705  return $str;
2706  }
2707  }
2708 
2714  function lc( $str, $first = false ) {
2715  if ( $first ) {
2716  if ( $this->isMultibyte( $str ) ) {
2717  return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2718  } else {
2719  return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2720  }
2721  } else {
2722  return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2723  }
2724  }
2725 
2730  function isMultibyte( $str ) {
2731  return strlen( $str ) !== mb_strlen( $str );
2732  }
2733 
2738  function ucwords( $str ) {
2739  if ( $this->isMultibyte( $str ) ) {
2740  $str = $this->lc( $str );
2741 
2742  // regexp to find first letter in each word (i.e. after each space)
2743  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2744 
2745  // function to use to capitalize a single char
2746  return preg_replace_callback(
2747  $replaceRegexp,
2748  [ $this, 'ucwordsCallbackMB' ],
2749  $str
2750  );
2751  } else {
2752  return ucwords( strtolower( $str ) );
2753  }
2754  }
2755 
2762  function ucwordbreaks( $str ) {
2763  if ( $this->isMultibyte( $str ) ) {
2764  $str = $this->lc( $str );
2765 
2766  // since \b doesn't work for UTF-8, we explicitely define word break chars
2767  $breaks = "[ \-\‍(\‍)\}\{\.,\?!]";
2768 
2769  // find first letter after word break
2770  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2771  "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2772 
2773  return preg_replace_callback(
2774  $replaceRegexp,
2775  [ $this, 'ucwordbreaksCallbackMB' ],
2776  $str
2777  );
2778  } else {
2779  return preg_replace_callback(
2780  '/\b([\w\x80-\xff]+)\b/',
2781  [ $this, 'ucwordbreaksCallbackAscii' ],
2782  $str
2783  );
2784  }
2785  }
2786 
2802  function caseFold( $s ) {
2803  return $this->uc( $s );
2804  }
2805 
2811  function checkTitleEncoding( $s ) {
2812  if ( is_array( $s ) ) {
2813  throw new MWException( 'Given array to checkTitleEncoding.' );
2814  }
2815  if ( StringUtils::isUtf8( $s ) ) {
2816  return $s;
2817  }
2818 
2819  return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
2820  }
2821 
2826  return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
2827  }
2828 
2837  function hasWordBreaks() {
2838  return true;
2839  }
2840 
2848  function segmentByWord( $string ) {
2849  return $string;
2850  }
2851 
2859  function normalizeForSearch( $string ) {
2860  return self::convertDoubleWidth( $string );
2861  }
2862 
2871  protected static function convertDoubleWidth( $string ) {
2872  static $full = null;
2873  static $half = null;
2874 
2875  if ( $full === null ) {
2876  $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2877  $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2878  $full = str_split( $fullWidth, 3 );
2879  $half = str_split( $halfWidth );
2880  }
2881 
2882  $string = str_replace( $full, $half, $string );
2883  return $string;
2884  }
2885 
2891  protected static function insertSpace( $string, $pattern ) {
2892  $string = preg_replace( $pattern, " $1 ", $string );
2893  $string = preg_replace( '/ +/', ' ', $string );
2894  return $string;
2895  }
2896 
2901  function convertForSearchResult( $termsArray ) {
2902  # some languages, e.g. Chinese, need to do a conversion
2903  # in order for search results to be displayed correctly
2904  return $termsArray;
2905  }
2906 
2913  function firstChar( $s ) {
2914  $matches = [];
2915  preg_match(
2916  '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2917  '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2918  $s,
2919  $matches
2920  );
2921 
2922  if ( isset( $matches[1] ) ) {
2923  if ( strlen( $matches[1] ) != 3 ) {
2924  return $matches[1];
2925  }
2926 
2927  // Break down Hangul syllables to grab the first jamo
2929  if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2930  return $matches[1];
2931  } elseif ( $code < 0xb098 ) {
2932  return "\xe3\x84\xb1";
2933  } elseif ( $code < 0xb2e4 ) {
2934  return "\xe3\x84\xb4";
2935  } elseif ( $code < 0xb77c ) {
2936  return "\xe3\x84\xb7";
2937  } elseif ( $code < 0xb9c8 ) {
2938  return "\xe3\x84\xb9";
2939  } elseif ( $code < 0xbc14 ) {
2940  return "\xe3\x85\x81";
2941  } elseif ( $code < 0xc0ac ) {
2942  return "\xe3\x85\x82";
2943  } elseif ( $code < 0xc544 ) {
2944  return "\xe3\x85\x85";
2945  } elseif ( $code < 0xc790 ) {
2946  return "\xe3\x85\x87";
2947  } elseif ( $code < 0xcc28 ) {
2948  return "\xe3\x85\x88";
2949  } elseif ( $code < 0xce74 ) {
2950  return "\xe3\x85\x8a";
2951  } elseif ( $code < 0xd0c0 ) {
2952  return "\xe3\x85\x8b";
2953  } elseif ( $code < 0xd30c ) {
2954  return "\xe3\x85\x8c";
2955  } elseif ( $code < 0xd558 ) {
2956  return "\xe3\x85\x8d";
2957  } else {
2958  return "\xe3\x85\x8e";
2959  }
2960  } else {
2961  return '';
2962  }
2963  }
2964 
2968  function initEncoding() {
2969  // No-op.
2970  }
2971 
2977  function recodeForEdit( $s ) {
2978  return $s;
2979  }
2980 
2986  function recodeInput( $s ) {
2987  return $s;
2988  }
2989 
3001  function normalize( $s ) {
3003  $s = UtfNormal\Validator::cleanUp( $s );
3004  if ( $wgAllUnicodeFixes ) {
3005  $s = $this->transformUsingPairFile( 'normalize-ar.ser', $s );
3006  $s = $this->transformUsingPairFile( 'normalize-ml.ser', $s );
3007  }
3008 
3009  return $s;
3010  }
3011 
3026  function transformUsingPairFile( $file, $string ) {
3027  if ( !isset( $this->transformData[$file] ) ) {
3028  $data = wfGetPrecompiledData( $file );
3029  if ( $data === false ) {
3030  throw new MWException( __METHOD__ . ": The transformation file $file is missing" );
3031  }
3032  $this->transformData[$file] = new ReplacementArray( $data );
3033  }
3034  return $this->transformData[$file]->replace( $string );
3035  }
3036 
3042  function isRTL() {
3043  return self::$dataCache->getItem( $this->mCode, 'rtl' );
3044  }
3045 
3050  function getDir() {
3051  return $this->isRTL() ? 'rtl' : 'ltr';
3052  }
3053 
3062  function alignStart() {
3063  return $this->isRTL() ? 'right' : 'left';
3064  }
3065 
3074  function alignEnd() {
3075  return $this->isRTL() ? 'left' : 'right';
3076  }
3077 
3089  function getDirMarkEntity( $opposite = false ) {
3090  if ( $opposite ) {
3091  return $this->isRTL() ? '&lrm;' : '&rlm;';
3092  }
3093  return $this->isRTL() ? '&rlm;' : '&lrm;';
3094  }
3095 
3106  function getDirMark( $opposite = false ) {
3107  $lrm = "\xE2\x80\x8E"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3108  $rlm = "\xE2\x80\x8F"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3109  if ( $opposite ) {
3110  return $this->isRTL() ? $lrm : $rlm;
3111  }
3112  return $this->isRTL() ? $rlm : $lrm;
3113  }
3114 
3118  function capitalizeAllNouns() {
3119  return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
3120  }
3121 
3129  function getArrow( $direction = 'forwards' ) {
3130  switch ( $direction ) {
3131  case 'forwards':
3132  return $this->isRTL() ? '←' : '→';
3133  case 'backwards':
3134  return $this->isRTL() ? '→' : '←';
3135  case 'left':
3136  return '←';
3137  case 'right':
3138  return '→';
3139  case 'up':
3140  return '↑';
3141  case 'down':
3142  return '↓';
3143  }
3144  }
3145 
3151  function linkPrefixExtension() {
3152  return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
3153  }
3154 
3159  function getMagicWords() {
3160  return self::$dataCache->getItem( $this->mCode, 'magicWords' );
3161  }
3162 
3166  protected function doMagicHook() {
3167  if ( $this->mMagicHookDone ) {
3168  return;
3169  }
3170  $this->mMagicHookDone = true;
3171  Hooks::run( 'LanguageGetMagic', [ &$this->mMagicExtensions, $this->getCode() ] );
3172  }
3173 
3179  function getMagic( $mw ) {
3180  // Saves a function call
3181  if ( !$this->mMagicHookDone ) {
3182  $this->doMagicHook();
3183  }
3184 
3185  if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3186  $rawEntry = $this->mMagicExtensions[$mw->mId];
3187  } else {
3188  $rawEntry = self::$dataCache->getSubitem(
3189  $this->mCode, 'magicWords', $mw->mId );
3190  }
3191 
3192  if ( !is_array( $rawEntry ) ) {
3193  wfWarn( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3194  } else {
3195  $mw->mCaseSensitive = $rawEntry[0];
3196  $mw->mSynonyms = array_slice( $rawEntry, 1 );
3197  }
3198  }
3199 
3205  function addMagicWordsByLang( $newWords ) {
3206  $fallbackChain = $this->getFallbackLanguages();
3207  $fallbackChain = array_reverse( $fallbackChain );
3208  foreach ( $fallbackChain as $code ) {
3209  if ( isset( $newWords[$code] ) ) {
3210  $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
3211  }
3212  }
3213  }
3214 
3221  // Cache aliases because it may be slow to load them
3222  if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3223  // Initialise array
3224  $this->mExtendedSpecialPageAliases =
3225  self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
3226  Hooks::run( 'LanguageGetSpecialPageAliases',
3227  [ &$this->mExtendedSpecialPageAliases, $this->getCode() ] );
3228  }
3229 
3231  }
3232 
3239  function emphasize( $text ) {
3240  return "<em>$text</em>";
3241  }
3242 
3265  public function formatNum( $number, $nocommafy = false ) {
3267  if ( !$nocommafy ) {
3268  $number = $this->commafy( $number );
3269  $s = $this->separatorTransformTable();
3270  if ( $s ) {
3271  $number = strtr( $number, $s );
3272  }
3273  }
3274 
3275  if ( $wgTranslateNumerals ) {
3276  $s = $this->digitTransformTable();
3277  if ( $s ) {
3278  $number = strtr( $number, $s );
3279  }
3280  }
3281 
3282  return (string)$number;
3283  }
3284 
3293  public function formatNumNoSeparators( $number ) {
3294  return $this->formatNum( $number, true );
3295  }
3296 
3301  public function parseFormattedNumber( $number ) {
3302  $s = $this->digitTransformTable();
3303  if ( $s ) {
3304  // eliminate empty array values such as ''. (T66347)
3305  $s = array_filter( $s );
3306  $number = strtr( $number, array_flip( $s ) );
3307  }
3308 
3309  $s = $this->separatorTransformTable();
3310  if ( $s ) {
3311  // eliminate empty array values such as ''. (T66347)
3312  $s = array_filter( $s );
3313  $number = strtr( $number, array_flip( $s ) );
3314  }
3315 
3316  $number = strtr( $number, [ ',' => '' ] );
3317  return $number;
3318  }
3319 
3326  function commafy( $number ) {
3329  if ( $number === null ) {
3330  return '';
3331  }
3332 
3333  if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) {
3334  // Default grouping is at thousands, use the same for ###,###,### pattern too.
3335  // In some languages it's conventional not to insert a thousands separator
3336  // in numbers that are four digits long (1000-9999).
3337  if ( $minimumGroupingDigits ) {
3338  // Number of '#' characters after last comma in the grouping pattern.
3339  // The pattern is hardcoded here, but this would vary for different patterns.
3340  $primaryGroupingSize = 3;
3341  // Maximum length of a number to suppress digit grouping for.
3342  $maximumLength = $minimumGroupingDigits + $primaryGroupingSize - 1;
3343  if ( preg_match( '/^\-?\d{1,' . $maximumLength . '}(\.\d+)?$/', $number ) ) {
3344  return $number;
3345  }
3346  }
3347  return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $number ) ) );
3348  } else {
3349  // Ref: http://cldr.unicode.org/translation/number-patterns
3350  $sign = "";
3351  if ( intval( $number ) < 0 ) {
3352  // For negative numbers apply the algorithm like positive number and add sign.
3353  $sign = "-";
3354  $number = substr( $number, 1 );
3355  }
3356  $integerPart = [];
3357  $decimalPart = [];
3358  $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches );
3359  preg_match( "/\d+/", $number, $integerPart );
3360  preg_match( "/\.\d*/", $number, $decimalPart );
3361  $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] : "";
3362  if ( $groupedNumber === $number ) {
3363  // the string does not have any number part. Eg: .12345
3364  return $sign . $groupedNumber;
3365  }
3366  $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3367  while ( $start > 0 ) {
3368  $match = $matches[0][$numMatches - 1];
3369  $matchLen = strlen( $match );
3370  $start = $end - $matchLen;
3371  if ( $start < 0 ) {
3372  $start = 0;
3373  }
3374  $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3375  $end = $start;
3376  if ( $numMatches > 1 ) {
3377  // use the last pattern for the rest of the number
3378  $numMatches--;
3379  }
3380  if ( $start > 0 ) {
3381  $groupedNumber = "," . $groupedNumber;
3382  }
3383  }
3384  return $sign . $groupedNumber;
3385  }
3386  }
3387 
3392  return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
3393  }
3394 
3398  function digitTransformTable() {
3399  return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
3400  }
3401 
3406  return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
3407  }
3408 
3413  return self::$dataCache->getItem( $this->mCode, 'minimumGroupingDigits' );
3414  }
3415 
3425  function listToText( array $l ) {
3426  $m = count( $l ) - 1;
3427  if ( $m < 0 ) {
3428  return '';
3429  }
3430  if ( $m > 0 ) {
3431  $and = $this->msg( 'and' )->escaped();
3432  $space = $this->msg( 'word-separator' )->escaped();
3433  if ( $m > 1 ) {
3434  $comma = $this->msg( 'comma-separator' )->escaped();
3435  }
3436  }
3437  $s = $l[$m];
3438  for ( $i = $m - 1; $i >= 0; $i-- ) {
3439  if ( $i == $m - 1 ) {
3440  $s = $l[$i] . $and . $space . $s;
3441  } else {
3442  $s = $l[$i] . $comma . $s;
3443  }
3444  }
3445  return $s;
3446  }
3447 
3454  function commaList( array $list ) {
3455  return implode(
3456  wfMessage( 'comma-separator' )->inLanguage( $this )->escaped(),
3457  $list
3458  );
3459  }
3460 
3467  function semicolonList( array $list ) {
3468  return implode(
3469  wfMessage( 'semicolon-separator' )->inLanguage( $this )->escaped(),
3470  $list
3471  );
3472  }
3473 
3479  function pipeList( array $list ) {
3480  return implode(
3481  wfMessage( 'pipe-separator' )->inLanguage( $this )->escaped(),
3482  $list
3483  );
3484  }
3485 
3503  function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3504  return $this->truncateForDatabase( $string, $length, $ellipsis, $adjustLength );
3505  }
3506 
3522  function truncateForDatabase( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3523  return $this->truncateInternal(
3524  $string, $length, $ellipsis, $adjustLength, 'strlen', 'substr'
3525  );
3526  }
3527 
3546  function truncateForVisual( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3547  // Passing encoding to mb_strlen and mb_substr is optional.
3548  // Encoding defaults to mb_internal_encoding(), which is set to UTF-8 in Setup.php, so
3549  // explicit specification of encoding is skipped.
3550  // Note: Both multibyte methods are callables invoked in truncateInternal.
3551  return $this->truncateInternal(
3552  $string, $length, $ellipsis, $adjustLength, 'mb_strlen', 'mb_substr'
3553  );
3554  }
3555 
3572  private function truncateInternal(
3573  $string, $length, $ellipsis = '...', $adjustLength = true, $measureLength, $getSubstring
3574  ) {
3575  if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
3576  throw new InvalidArgumentException( 'Invalid callback provided' );
3577  }
3578 
3579  # Check if there is no need to truncate
3580  if ( $measureLength( $string ) <= abs( $length ) ) {
3581  return $string; // no need to truncate
3582  }
3583 
3584  # Use the localized ellipsis character
3585  if ( $ellipsis == '...' ) {
3586  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3587  }
3588  if ( $length == 0 ) {
3589  return $ellipsis; // convention
3590  }
3591 
3592  $stringOriginal = $string;
3593  # If ellipsis length is >= $length then we can't apply $adjustLength
3594  if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3595  $string = $ellipsis; // this can be slightly unexpected
3596  # Otherwise, truncate and add ellipsis...
3597  } else {
3598  $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3599  if ( $length > 0 ) {
3600  $length -= $ellipsisLength;
3601  $string = $getSubstring( $string, 0, $length ); // xyz...
3602  $string = $this->removeBadCharLast( $string );
3603  $string = rtrim( $string );
3604  $string = $string . $ellipsis;
3605  } else {
3606  $length += $ellipsisLength;
3607  $string = $getSubstring( $string, $length ); // ...xyz
3608  $string = $this->removeBadCharFirst( $string );
3609  $string = ltrim( $string );
3610  $string = $ellipsis . $string;
3611  }
3612  }
3613 
3614  # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3615  # This check is *not* redundant if $adjustLength, due to the single case where
3616  # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3617  if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3618  return $string;
3619  } else {
3620  return $stringOriginal;
3621  }
3622  }
3623 
3631  protected function removeBadCharLast( $string ) {
3632  if ( $string != '' ) {
3633  $char = ord( $string[strlen( $string ) - 1] );
3634  $m = [];
3635  if ( $char >= 0xc0 ) {
3636  # We got the first byte only of a multibyte char; remove it.
3637  $string = substr( $string, 0, -1 );
3638  } elseif ( $char >= 0x80 &&
3639  // Use the /s modifier (PCRE_DOTALL) so (.*) also matches newlines
3640  preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3641  '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3642  ) {
3643  # We chopped in the middle of a character; remove it
3644  $string = $m[1];
3645  }
3646  }
3647  return $string;
3648  }
3649 
3657  protected function removeBadCharFirst( $string ) {
3658  if ( $string != '' ) {
3659  $char = ord( $string[0] );
3660  if ( $char >= 0x80 && $char < 0xc0 ) {
3661  # We chopped in the middle of a character; remove the whole thing
3662  $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
3663  }
3664  }
3665  return $string;
3666  }
3667 
3683  function truncateHtml( $text, $length, $ellipsis = '...' ) {
3684  # Use the localized ellipsis character
3685  if ( $ellipsis == '...' ) {
3686  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3687  }
3688  # Check if there is clearly no need to truncate
3689  if ( $length <= 0 ) {
3690  return $ellipsis; // no text shown, nothing to format (convention)
3691  } elseif ( strlen( $text ) <= $length ) {
3692  return $text; // string short enough even *with* HTML (short-circuit)
3693  }
3694 
3695  $dispLen = 0; // innerHTML legth so far
3696  $testingEllipsis = false; // checking if ellipses will make string longer/equal?
3697  $tagType = 0; // 0-open, 1-close
3698  $bracketState = 0; // 1-tag start, 2-tag name, 0-neither
3699  $entityState = 0; // 0-not entity, 1-entity
3700  $tag = $ret = ''; // accumulated tag name, accumulated result string
3701  $openTags = []; // open tag stack
3702  $maybeState = null; // possible truncation state
3703 
3704  $textLen = strlen( $text );
3705  $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated
3706  for ( $pos = 0; true; ++$pos ) {
3707  # Consider truncation once the display length has reached the maximim.
3708  # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3709  # Check that we're not in the middle of a bracket/entity...
3710  if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3711  if ( !$testingEllipsis ) {
3712  $testingEllipsis = true;
3713  # Save where we are; we will truncate here unless there turn out to
3714  # be so few remaining characters that truncation is not necessary.
3715  if ( !$maybeState ) { // already saved? ($neLength = 0 case)
3716  $maybeState = [ $ret, $openTags ]; // save state
3717  }
3718  } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3719  # String in fact does need truncation, the truncation point was OK.
3720  list( $ret, $openTags ) = $maybeState; // reload state
3721  $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix
3722  $ret .= $ellipsis; // add ellipsis
3723  break;
3724  }
3725  }
3726  if ( $pos >= $textLen ) {
3727  break; // extra iteration just for above checks
3728  }
3729 
3730  # Read the next char...
3731  $ch = $text[$pos];
3732  $lastCh = $pos ? $text[$pos - 1] : '';
3733  $ret .= $ch; // add to result string
3734  if ( $ch == '<' ) {
3735  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML
3736  $entityState = 0; // for bad HTML
3737  $bracketState = 1; // tag started (checking for backslash)
3738  } elseif ( $ch == '>' ) {
3739  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags );
3740  $entityState = 0; // for bad HTML
3741  $bracketState = 0; // out of brackets
3742  } elseif ( $bracketState == 1 ) {
3743  if ( $ch == '/' ) {
3744  $tagType = 1; // close tag (e.g. "</span>")
3745  } else {
3746  $tagType = 0; // open tag (e.g. "<span>")
3747  $tag .= $ch;
3748  }
3749  $bracketState = 2; // building tag name
3750  } elseif ( $bracketState == 2 ) {
3751  if ( $ch != ' ' ) {
3752  $tag .= $ch;
3753  } else {
3754  // Name found (e.g. "<a href=..."), add on tag attributes...
3755  $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 );
3756  }
3757  } elseif ( $bracketState == 0 ) {
3758  if ( $entityState ) {
3759  if ( $ch == ';' ) {
3760  $entityState = 0;
3761  $dispLen++; // entity is one displayed char
3762  }
3763  } else {
3764  if ( $neLength == 0 && !$maybeState ) {
3765  // Save state without $ch. We want to *hit* the first
3766  // display char (to get tags) but not *use* it if truncating.
3767  $maybeState = [ substr( $ret, 0, -1 ), $openTags ];
3768  }
3769  if ( $ch == '&' ) {
3770  $entityState = 1; // entity found, (e.g. "&#160;")
3771  } else {
3772  $dispLen++; // this char is displayed
3773  // Add the next $max display text chars after this in one swoop...
3774  $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3775  $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max );
3776  $dispLen += $skipped;
3777  $pos += $skipped;
3778  }
3779  }
3780  }
3781  }
3782  // Close the last tag if left unclosed by bad HTML
3783  $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags );
3784  while ( count( $openTags ) > 0 ) {
3785  $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags
3786  }
3787  return $ret;
3788  }
3789 
3801  private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
3802  if ( $len === null ) {
3803  $len = -1; // -1 means "no limit" for strcspn
3804  } elseif ( $len < 0 ) {
3805  $len = 0; // sanity
3806  }
3807  $skipCount = 0;
3808  if ( $start < strlen( $text ) ) {
3809  $skipCount = strcspn( $text, $search, $start, $len );
3810  $ret .= substr( $text, $start, $skipCount );
3811  }
3812  return $skipCount;
3813  }
3814 
3824  private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
3825  $tag = ltrim( $tag );
3826  if ( $tag != '' ) {
3827  if ( $tagType == 0 && $lastCh != '/' ) {
3828  $openTags[] = $tag; // tag opened (didn't close itself)
3829  } elseif ( $tagType == 1 ) {
3830  if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3831  array_pop( $openTags ); // tag closed
3832  }
3833  }
3834  $tag = '';
3835  }
3836  }
3837 
3846  function convertGrammar( $word, $case ) {
3848  if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
3849  return $wgGrammarForms[$this->getCode()][$case][$word];
3850  }
3851 
3853 
3854  if ( isset( $grammarTransformations[$case] ) ) {
3855  $forms = $grammarTransformations[$case];
3856 
3857  // Some names of grammar rules are aliases for other rules.
3858  // In such cases the value is a string rather than object,
3859  // so load the actual rules.
3860  if ( is_string( $forms ) ) {
3861  $forms = $grammarTransformations[$forms];
3862  }
3863 
3864  foreach ( array_values( $forms ) as $rule ) {
3865  $form = $rule[0];
3866 
3867  if ( $form === '@metadata' ) {
3868  continue;
3869  }
3870 
3871  $replacement = $rule[1];
3872 
3873  $regex = '/' . addcslashes( $form, '/' ) . '/u';
3874  $patternMatches = preg_match( $regex, $word );
3875 
3876  if ( $patternMatches === false ) {
3877  wfLogWarning(
3878  'An error occurred while processing grammar. ' .
3879  "Word: '$word'. Regex: /$form/."
3880  );
3881  } elseif ( $patternMatches === 1 ) {
3882  $word = preg_replace( $regex, $replacement, $word );
3883 
3884  break;
3885  }
3886  }
3887  }
3888 
3889  return $word;
3890  }
3891 
3897  function getGrammarForms() {
3899  if ( isset( $wgGrammarForms[$this->getCode()] )
3900  && is_array( $wgGrammarForms[$this->getCode()] )
3901  ) {
3902  return $wgGrammarForms[$this->getCode()];
3903  }
3904 
3905  return [];
3906  }
3907 
3917  public function getGrammarTransformations() {
3918  $languageCode = $this->getCode();
3919 
3920  if ( self::$grammarTransformations === null ) {
3921  self::$grammarTransformations = new MapCacheLRU( 10 );
3922  }
3923 
3924  if ( self::$grammarTransformations->has( $languageCode ) ) {
3925  return self::$grammarTransformations->get( $languageCode );
3926  }
3927 
3928  $data = [];
3929 
3930  $grammarDataFile = __DIR__ . "/data/grammarTransformations/$languageCode.json";
3931  if ( is_readable( $grammarDataFile ) ) {
3932  $data = FormatJson::decode(
3933  file_get_contents( $grammarDataFile ),
3934  true
3935  );
3936 
3937  if ( $data === null ) {
3938  throw new MWException( "Invalid grammar data for \"$languageCode\"." );
3939  }
3940 
3941  self::$grammarTransformations->set( $languageCode, $data );
3942  }
3943 
3944  return $data;
3945  }
3946 
3966  function gender( $gender, $forms ) {
3967  if ( !count( $forms ) ) {
3968  return '';
3969  }
3970  $forms = $this->preConvertPlural( $forms, 2 );
3971  if ( $gender === 'male' ) {
3972  return $forms[0];
3973  }
3974  if ( $gender === 'female' ) {
3975  return $forms[1];
3976  }
3977  return isset( $forms[2] ) ? $forms[2] : $forms[0];
3978  }
3979 
3995  function convertPlural( $count, $forms ) {
3996  // Handle explicit n=pluralform cases
3997  $forms = $this->handleExplicitPluralForms( $count, $forms );
3998  if ( is_string( $forms ) ) {
3999  return $forms;
4000  }
4001  if ( !count( $forms ) ) {
4002  return '';
4003  }
4004 
4005  $pluralForm = $this->getPluralRuleIndexNumber( $count );
4006  $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4007  return $forms[$pluralForm];
4008  }
4009 
4025  protected function handleExplicitPluralForms( $count, array $forms ) {
4026  foreach ( $forms as $index => $form ) {
4027  if ( preg_match( '/\d+=/i', $form ) ) {
4028  $pos = strpos( $form, '=' );
4029  if ( substr( $form, 0, $pos ) === (string)$count ) {
4030  return substr( $form, $pos + 1 );
4031  }
4032  unset( $forms[$index] );
4033  }
4034  }
4035  return array_values( $forms );
4036  }
4037 
4046  protected function preConvertPlural( /* Array */ $forms, $count ) {
4047  while ( count( $forms ) < $count ) {
4048  $forms[] = $forms[count( $forms ) - 1];
4049  }
4050  return $forms;
4051  }
4052 
4069  public function embedBidi( $text = '' ) {
4070  $dir = self::strongDirFromContent( $text );
4071  if ( $dir === 'ltr' ) {
4072  // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING
4073  return self::$lre . $text . self::$pdf;
4074  }
4075  if ( $dir === 'rtl' ) {
4076  // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING
4077  return self::$rle . $text . self::$pdf;
4078  }
4079  // No strong directionality: do not wrap
4080  return $text;
4081  }
4082 
4096  function translateBlockExpiry( $str, User $user = null, $now = 0 ) {
4097  $duration = SpecialBlock::getSuggestedDurations( $this );
4098  foreach ( $duration as $show => $value ) {
4099  if ( strcmp( $str, $value ) == 0 ) {
4100  return trim( $show );
4101  }
4102  }
4103 
4104  if ( wfIsInfinity( $str ) ) {
4105  foreach ( $duration as $show => $value ) {
4106  if ( wfIsInfinity( $value ) ) {
4107  return trim( $show );
4108  }
4109  }
4110  }
4111 
4112  // If all else fails, return a standard duration or timestamp description.
4113  $time = strtotime( $str, $now );
4114  if ( $time === false ) { // Unknown format. Return it as-is in case.
4115  return $str;
4116  } elseif ( $time !== strtotime( $str, $now + 1 ) ) { // It's a relative timestamp.
4117  // The result differs based on current time, so the difference
4118  // is a fixed duration length.
4119  return $this->formatDuration( $time - $now );
4120  } else { // It's an absolute timestamp.
4121  if ( $time === 0 ) {
4122  // wfTimestamp() handles 0 as current time instead of epoch.
4123  $time = '19700101000000';
4124  }
4125  if ( $user ) {
4126  return $this->userTimeAndDate( $time, $user );
4127  }
4128  return $this->timeanddate( $time );
4129  }
4130  }
4131 
4139  public function segmentForDiff( $text ) {
4140  return $text;
4141  }
4142 
4149  public function unsegmentForDiff( $text ) {
4150  return $text;
4151  }
4152 
4159  public function getConverter() {
4160  return $this->mConverter;
4161  }
4162 
4171  public function autoConvert( $text, $variant = false ) {
4172  return $this->mConverter->autoConvert( $text, $variant );
4173  }
4174 
4181  public function autoConvertToAllVariants( $text ) {
4182  return $this->mConverter->autoConvertToAllVariants( $text );
4183  }
4184 
4191  public function convert( $text ) {
4192  return $this->mConverter->convert( $text );
4193  }
4194 
4201  public function convertTitle( $title ) {
4202  return $this->mConverter->convertTitle( $title );
4203  }
4204 
4213  public function convertNamespace( $ns, $variant = null ) {
4214  return $this->mConverter->convertNamespace( $ns, $variant );
4215  }
4216 
4222  public function hasVariants() {
4223  return count( $this->getVariants() ) > 1;
4224  }
4225 
4233  public function hasVariant( $variant ) {
4234  return (bool)$this->mConverter->validateVariant( $variant );
4235  }
4236 
4244  public function convertHtml( $text, $isTitle = false ) {
4245  return htmlspecialchars( $this->convert( $text, $isTitle ) );
4246  }
4247 
4252  public function convertCategoryKey( $key ) {
4253  return $this->mConverter->convertCategoryKey( $key );
4254  }
4255 
4262  public function getVariants() {
4263  return $this->mConverter->getVariants();
4264  }
4265 
4269  public function getPreferredVariant() {
4270  return $this->mConverter->getPreferredVariant();
4271  }
4272 
4276  public function getDefaultVariant() {
4277  return $this->mConverter->getDefaultVariant();
4278  }
4279 
4283  public function getURLVariant() {
4284  return $this->mConverter->getURLVariant();
4285  }
4286 
4299  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
4300  $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
4301  }
4302 
4309  function getExtraHashOptions() {
4310  return $this->mConverter->getExtraHashOptions();
4311  }
4312 
4320  public function getParsedTitle() {
4321  return $this->mConverter->getParsedTitle();
4322  }
4323 
4330  public function updateConversionTable( Title $title ) {
4331  $this->mConverter->updateConversionTable( $title );
4332  }
4333 
4346  public function markNoConversion( $text, $noParse = false ) {
4347  // Excluding protocal-relative URLs may avoid many false positives.
4348  if ( $noParse || preg_match( '/^(?:' . wfUrlProtocolsWithoutProtRel() . ')/', $text ) ) {
4349  return $this->mConverter->markNoConversion( $text );
4350  } else {
4351  return $text;
4352  }
4353  }
4354 
4361  public function linkTrail() {
4362  return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
4363  }
4364 
4371  public function linkPrefixCharset() {
4372  return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
4373  }
4374 
4382  public function getParentLanguage() {
4383  if ( $this->mParentLanguage !== false ) {
4384  return $this->mParentLanguage;
4385  }
4386 
4387  $code = explode( '-', $this->getCode() )[0];
4388  if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
4389  $this->mParentLanguage = null;
4390  return null;
4391  }
4392  $lang = self::factory( $code );
4393  if ( !$lang->hasVariant( $this->getCode() ) ) {
4394  $this->mParentLanguage = null;
4395  return null;
4396  }
4397 
4398  $this->mParentLanguage = $lang;
4399  return $lang;
4400  }
4401 
4409  public function equals( Language $lang ) {
4410  return $lang->getCode() === $this->mCode;
4411  }
4412 
4421  public function getCode() {
4422  return $this->mCode;
4423  }
4424 
4435  public function getHtmlCode() {
4436  if ( is_null( $this->mHtmlCode ) ) {
4437  $this->mHtmlCode = LanguageCode::bcp47( $this->getCode() );
4438  }
4439  return $this->mHtmlCode;
4440  }
4441 
4445  public function setCode( $code ) {
4446  $this->mCode = $code;
4447  // Ensure we don't leave incorrect cached data lying around
4448  $this->mHtmlCode = null;
4449  $this->mParentLanguage = false;
4450  }
4451 
4459  public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
4460  $m = null;
4461  preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' .
4462  preg_quote( $suffix, '/' ) . '/', $filename, $m );
4463  if ( !count( $m ) ) {
4464  return false;
4465  }
4466  return str_replace( '_', '-', strtolower( $m[1] ) );
4467  }
4468 
4474  public static function classFromCode( $code, $fallback = true ) {
4475  if ( $fallback && $code == 'en' ) {
4476  return 'Language';
4477  } else {
4478  return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
4479  }
4480  }
4481 
4490  public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
4491  if ( !self::isValidBuiltInCode( $code ) ) {
4492  throw new MWException( "Invalid language code \"$code\"" );
4493  }
4494 
4495  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
4496  }
4497 
4502  public static function getMessagesFileName( $code ) {
4503  global $IP;
4504  $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
4505  Hooks::run( 'Language::getMessagesFileName', [ $code, &$file ] );
4506  return $file;
4507  }
4508 
4515  public static function getJsonMessagesFileName( $code ) {
4516  global $IP;
4517 
4518  if ( !self::isValidBuiltInCode( $code ) ) {
4519  throw new MWException( "Invalid language code \"$code\"" );
4520  }
4521 
4522  return "$IP/languages/i18n/$code.json";
4523  }
4524 
4532  public static function getFallbackFor( $code ) {
4533  $fallbacks = self::getFallbacksFor( $code );
4534  if ( $fallbacks ) {
4535  return $fallbacks[0];
4536  }
4537  return false;
4538  }
4539 
4547  public static function getFallbacksFor( $code ) {
4548  if ( $code === 'en' || !self::isValidBuiltInCode( $code ) ) {
4549  return [];
4550  }
4551  // For unknown languages, fallbackSequence returns an empty array,
4552  // hardcode fallback to 'en' in that case.
4553  return self::getLocalisationCache()->getItem( $code, 'fallbackSequence' ) ?: [ 'en' ];
4554  }
4555 
4564  public static function getFallbacksIncludingSiteLanguage( $code ) {
4566 
4567  // Usually, we will only store a tiny number of fallback chains, so we
4568  // keep them in static memory.
4569  $cacheKey = "{$code}-{$wgLanguageCode}";
4570 
4571  if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4572  $fallbacks = self::getFallbacksFor( $code );
4573 
4574  // Append the site's fallback chain, including the site language itself
4575  $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
4576  array_unshift( $siteFallbacks, $wgLanguageCode );
4577 
4578  // Eliminate any languages already included in the chain
4579  $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4580 
4581  self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4582  }
4583  return self::$fallbackLanguageCache[$cacheKey];
4584  }
4585 
4595  public static function getMessagesFor( $code ) {
4596  return self::getLocalisationCache()->getItem( $code, 'messages' );
4597  }
4598 
4607  public static function getMessageFor( $key, $code ) {
4608  return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
4609  }
4610 
4619  public static function getMessageKeysFor( $code ) {
4620  return self::getLocalisationCache()->getSubitemList( $code, 'messages' );
4621  }
4622 
4627  function fixVariableInNamespace( $talk ) {
4628  if ( strpos( $talk, '$1' ) === false ) {
4629  return $talk;
4630  }
4631 
4633  $talk = str_replace( '$1', $wgMetaNamespace, $talk );
4634 
4635  # Allow grammar transformations
4636  # Allowing full message-style parsing would make simple requests
4637  # such as action=raw much more expensive than they need to be.
4638  # This will hopefully cover most cases.
4639  $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
4640  [ $this, 'replaceGrammarInNamespace' ], $talk );
4641  return str_replace( ' ', '_', $talk );
4642  }
4643 
4648  function replaceGrammarInNamespace( $m ) {
4649  return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
4650  }
4651 
4662  public function formatExpiry( $expiry, $format = true, $infinity = 'infinity' ) {
4663  static $dbInfinity;
4664  if ( $dbInfinity === null ) {
4665  $dbInfinity = wfGetDB( DB_REPLICA )->getInfinity();
4666  }
4667 
4668  if ( $expiry == '' || $expiry === 'infinity' || $expiry == $dbInfinity ) {
4669  return $format === true
4670  ? $this->getMessageFromDB( 'infiniteblock' )
4671  : $infinity;
4672  } else {
4673  return $format === true
4674  ? $this->timeanddate( $expiry, /* User preference timezone */ true )
4675  : wfTimestamp( $format, $expiry );
4676  }
4677  }
4678 
4692  function formatTimePeriod( $seconds, $format = [] ) {
4693  if ( !is_array( $format ) ) {
4694  $format = [ 'avoid' => $format ]; // For backwards compatibility
4695  }
4696  if ( !isset( $format['avoid'] ) ) {
4697  $format['avoid'] = false;
4698  }
4699  if ( !isset( $format['noabbrevs'] ) ) {
4700  $format['noabbrevs'] = false;
4701  }
4702  $secondsMsg = wfMessage(
4703  $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this );
4704  $minutesMsg = wfMessage(
4705  $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this );
4706  $hoursMsg = wfMessage(
4707  $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this );
4708  $daysMsg = wfMessage(
4709  $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this );
4710 
4711  if ( round( $seconds * 10 ) < 100 ) {
4712  $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) );
4713  $s = $secondsMsg->params( $s )->text();
4714  } elseif ( round( $seconds ) < 60 ) {
4715  $s = $this->formatNum( round( $seconds ) );
4716  $s = $secondsMsg->params( $s )->text();
4717  } elseif ( round( $seconds ) < 3600 ) {
4718  $minutes = floor( $seconds / 60 );
4719  $secondsPart = round( fmod( $seconds, 60 ) );
4720  if ( $secondsPart == 60 ) {
4721  $secondsPart = 0;
4722  $minutes++;
4723  }
4724  $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4725  $s .= ' ';
4726  $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4727  } elseif ( round( $seconds ) <= 2 * 86400 ) {
4728  $hours = floor( $seconds / 3600 );
4729  $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4730  $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4731  if ( $secondsPart == 60 ) {
4732  $secondsPart = 0;
4733  $minutes++;
4734  }
4735  if ( $minutes == 60 ) {
4736  $minutes = 0;
4737  $hours++;
4738  }
4739  $s = $hoursMsg->params( $this->formatNum( $hours ) )->text();
4740  $s .= ' ';
4741  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4742  if ( !in_array( $format['avoid'], [ 'avoidseconds', 'avoidminutes' ] ) ) {
4743  $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4744  }
4745  } else {
4746  $days = floor( $seconds / 86400 );
4747  if ( $format['avoid'] === 'avoidminutes' ) {
4748  $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4749  if ( $hours == 24 ) {
4750  $hours = 0;
4751  $days++;
4752  }
4753  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4754  $s .= ' ';
4755  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4756  } elseif ( $format['avoid'] === 'avoidseconds' ) {
4757  $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4758  $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4759  if ( $minutes == 60 ) {
4760  $minutes = 0;
4761  $hours++;
4762  }
4763  if ( $hours == 24 ) {
4764  $hours = 0;
4765  $days++;
4766  }
4767  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4768  $s .= ' ';
4769  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4770  $s .= ' ';
4771  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4772  } else {
4773  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4774  $s .= ' ';
4775  $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format );
4776  }
4777  }
4778  return $s;
4779  }
4780 
4792  function formatBitrate( $bps ) {
4793  return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" );
4794  }
4795 
4802  function formatComputingNumbers( $size, $boundary, $messageKey ) {
4803  if ( $size <= 0 ) {
4804  return str_replace( '$1', $this->formatNum( $size ),
4805  $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) )
4806  );
4807  }
4808  $sizes = [ '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' ];
4809  $index = 0;
4810 
4811  $maxIndex = count( $sizes ) - 1;
4812  while ( $size >= $boundary && $index < $maxIndex ) {
4813  $index++;
4814  $size /= $boundary;
4815  }
4816 
4817  // For small sizes no decimal places necessary
4818  $round = 0;
4819  if ( $index > 1 ) {
4820  // For MB and bigger two decimal places are smarter
4821  $round = 2;
4822  }
4823  $msg = str_replace( '$1', $sizes[$index], $messageKey );
4824 
4825  $size = round( $size, $round );
4826  $text = $this->getMessageFromDB( $msg );
4827  return str_replace( '$1', $this->formatNum( $size ), $text );
4828  }
4829 
4840  function formatSize( $size ) {
4841  return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" );
4842  }
4843 
4853  function specialList( $page, $details, $oppositedm = true ) {
4854  if ( !$details ) {
4855  return $page;
4856  }
4857 
4858  $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) . $this->getDirMark();
4859  return $page .
4860  $dirmark .
4861  $this->msg( 'word-separator' )->escaped() .
4862  $this->msg( 'parentheses' )->rawParams( $details )->escaped();
4863  }
4864 
4875  public function viewPrevNext( Title $title, $offset, $limit,
4876  array $query = [], $atend = false
4877  ) {
4878  // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
4879 
4880  # Make 'previous' link
4881  $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4882  if ( $offset > 0 ) {
4883  $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit,
4884  $query, $prev, 'prevn-title', 'mw-prevlink' );
4885  } else {
4886  $plink = htmlspecialchars( $prev );
4887  }
4888 
4889  # Make 'next' link
4890  $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4891  if ( $atend ) {
4892  $nlink = htmlspecialchars( $next );
4893  } else {
4894  $nlink = $this->numLink( $title, $offset + $limit, $limit,
4895  $query, $next, 'nextn-title', 'mw-nextlink' );
4896  }
4897 
4898  # Make links to set number of items per page
4899  $numLinks = [];
4900  foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
4901  $numLinks[] = $this->numLink( $title, $offset, $num,
4902  $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' );
4903  }
4904 
4905  return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title
4906  )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped();
4907  }
4908 
4921  private function numLink( Title $title, $offset, $limit, array $query, $link,
4922  $tooltipMsg, $class
4923  ) {
4924  $query = [ 'limit' => $limit, 'offset' => $offset ] + $query;
4925  $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
4926  ->numParams( $limit )->text();
4927 
4928  return Html::element( 'a', [ 'href' => $title->getLocalURL( $query ),
4929  'title' => $tooltip, 'class' => $class ], $link );
4930  }
4931 
4937  public function getConvRuleTitle() {
4938  return $this->mConverter->getConvRuleTitle();
4939  }
4940 
4946  public function getCompiledPluralRules() {
4947  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
4948  $fallbacks = self::getFallbacksFor( $this->mCode );
4949  if ( !$pluralRules ) {
4950  foreach ( $fallbacks as $fallbackCode ) {
4951  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
4952  if ( $pluralRules ) {
4953  break;
4954  }
4955  }
4956  }
4957  return $pluralRules;
4958  }
4959 
4965  public function getPluralRules() {
4966  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
4967  $fallbacks = self::getFallbacksFor( $this->mCode );
4968  if ( !$pluralRules ) {
4969  foreach ( $fallbacks as $fallbackCode ) {
4970  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
4971  if ( $pluralRules ) {
4972  break;
4973  }
4974  }
4975  }
4976  return $pluralRules;
4977  }
4978 
4984  public function getPluralRuleTypes() {
4985  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
4986  $fallbacks = self::getFallbacksFor( $this->mCode );
4987  if ( !$pluralRuleTypes ) {
4988  foreach ( $fallbacks as $fallbackCode ) {
4989  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
4990  if ( $pluralRuleTypes ) {
4991  break;
4992  }
4993  }
4994  }
4995  return $pluralRuleTypes;
4996  }
4997 
5003  public function getPluralRuleIndexNumber( $number ) {
5004  $pluralRules = $this->getCompiledPluralRules();
5005  $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5006  return $form;
5007  }
5008 
5017  public function getPluralRuleType( $number ) {
5018  $index = $this->getPluralRuleIndexNumber( $number );
5019  $pluralRuleTypes = $this->getPluralRuleTypes();
5020  if ( isset( $pluralRuleTypes[$index] ) ) {
5021  return $pluralRuleTypes[$index];
5022  } else {
5023  return 'other';
5024  }
5025  }
5026 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1762
Language\$mCode
$mCode
Definition: Language.php:41
Language\internalUserTimeAndDate
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
Definition: Language.php:2388
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1795
Language\getFallbacksFor
static getFallbacksFor( $code)
Get the ordered list of fallback languages.
Definition: Language.php:4547
Language\getCodeFromFileName
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
Definition: Language.php:4459
Language\getExtraHashOptions
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
Definition: Language.php:4309
$user
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 account $user
Definition: hooks.txt:247
Language\lc
lc( $str, $first=false)
Definition: Language.php:2714
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
Language\parseFormattedNumber
parseFormattedNumber( $number)
Definition: Language.php:3301
Language\getURLVariant
getURLVariant()
Definition: Language.php:4283
Language\getVariantname
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
Definition: Language.php:713
Language\replaceGrammarInNamespace
replaceGrammarInNamespace( $m)
Definition: Language.php:4648
$wgUser
$wgUser
Definition: Setup.php:902
Language\linkPrefixExtension
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
Definition: Language.php:3151
StringUtils\isUtf8
static isUtf8( $value)
Test whether a string is valid UTF-8.
Definition: StringUtils.php:41
Language\sprintfDate
sprintfDate( $format, $ts, DateTimeZone $zone=null, &$ttl='unused')
This is a workalike of PHP's date() function, but with better internationalisation,...
Definition: Language.php:1102
Language\truncate_endBracket
truncate_endBracket(&$tag, $tagType, $lastCh, &$openTags)
truncateHtml() helper function (a) push or pop $tag from $openTags as needed (b) clear $tag value
Definition: Language.php:3824
Language\commaList
commaList(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Definition: Language.php:3454
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
Language\semicolonList
semicolonList(array $list)
Take a list of strings and build a locale-friendly semicolon-separated list, using the local semicolo...
Definition: Language.php:3467
Language\$IRANIAN_DAYS
static $IRANIAN_DAYS
Definition: Language.php:1572
Language\$mIranianCalendarMonthMsgs
static $mIranianCalendarMonthMsgs
Definition: Language.php:88
Language\getIranianCalendarMonthName
getIranianCalendarMonthName( $key)
Definition: Language.php:988
Language\$mMonthAbbrevMsgs
static $mMonthAbbrevMsgs
Definition: Language.php:83
Language\formatSize
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
Definition: Language.php:4840
Language\separatorTransformTable
separatorTransformTable()
Definition: Language.php:3405
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
wfMessage
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 "&lt
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Language\resetNamespaces
resetNamespaces()
Resets all of the namespace caches.
Definition: Language.php:512
Language\getConverter
getConverter()
Return the LanguageConverter used in the Language.
Definition: Language.php:4159
Language\hasVariants
hasVariants()
Check if this is a language with variants.
Definition: Language.php:4222
Language\$mWeekdayAbbrevMsgs
static $mWeekdayAbbrevMsgs
Definition: Language.php:69
Language\preConvertPlural
preConvertPlural($forms, $count)
Checks that convertPlural was given an array and pads it to requested amount of forms by copying the ...
Definition: Language.php:4046
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
Language\minimumGroupingDigits
minimumGroupingDigits()
Definition: Language.php:3412
Language\segmentByWord
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
Definition: Language.php:2848
Language\iconv
iconv( $in, $out, $string)
Definition: Language.php:2615
Language\getMessageFromDB
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
Definition: Language.php:908
Language\$GREG_DAYS
static $GREG_DAYS
Definition: Language.php:1571
Language\convert
convert( $text)
convert text to different variants of a language.
Definition: Language.php:4191
string
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
Definition: hooks.txt:181
Language\truncate
truncate( $string, $length, $ellipsis='...', $adjustLength=true)
This method is deprecated since 1.31 and kept as alias for truncateForDatabase, which has replaced it...
Definition: Language.php:3503
Language\updateConversionTable
updateConversionTable(Title $title)
Refresh the cache of conversion tables when MediaWiki:Conversiontable* is updated.
Definition: Language.php:4330
Language\$languageNameCache
static HashBagOStuff null $languageNameCache
Cache for language names.
Definition: Language.php:152
Language\convertGrammar
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
Definition: Language.php:3846
$fallback
$fallback
Definition: MessagesAb.php:11
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1980
$ret
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
Definition: hooks.txt:2005
Language\truncateForVisual
truncateForVisual( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified number of characters, appending an optional string (e....
Definition: Language.php:3546
Language\$mMonthGenMsgs
static $mMonthGenMsgs
Definition: Language.php:78
Language\timeanddate
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2292
MediaWikiTitleCodec\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: MediaWikiTitleCodec.php:473
$out
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
Definition: hooks.txt:864
Language\getHebrewCalendarMonthNameGen
getHebrewCalendarMonthNameGen( $key)
Definition: Language.php:1004
Language\$mMagicHookDone
$mMagicHookDone
Definition: Language.php:42
Language\$mConverter
LanguageConverter $mConverter
Definition: Language.php:39
$wgLangObjCacheSize
$wgLangObjCacheSize
Language cache size, or really how many languages can we handle simultaneously without degrading to c...
Definition: DefaultSettings.php:2893
Language\listToText
listToText(array $l)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Definition: Language.php:3425
Language\getMonthNamesArray
getMonthNamesArray()
Definition: Language.php:933
$s
$s
Definition: mergeMessageFileList.php:187
Language\$mHijriCalendarMonthMsgs
static $mHijriCalendarMonthMsgs
Definition: Language.php:111
Language\$namespaceNames
array null $namespaceNames
Definition: Language.php:49
Language\getPreferredVariant
getPreferredVariant()
Definition: Language.php:4269
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1150
Language\getMonthAbbreviation
getMonthAbbreviation( $key)
Definition: Language.php:953
Language\ucwordbreaksCallbackAscii
ucwordbreaksCallbackAscii( $matches)
Definition: Language.php:2632
$digitGroupingPattern
$digitGroupingPattern
Definition: MessagesAs.php:167
Language\recodeForEdit
recodeForEdit( $s)
Definition: Language.php:2977
Language\markNoConversion
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
Definition: Language.php:4346
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:3477
Language\getDefaultVariant
getDefaultVariant()
Definition: Language.php:4276
Language\fallback8bitEncoding
fallback8bitEncoding()
Definition: Language.php:2825
Language\getFileName
static getFileName( $prefix='Language', $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: Language.php:4490
Language\embedBidi
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
Definition: Language.php:4069
Language\formatTimePeriod
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
Definition: Language.php:4692
Language\formatBitrate
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
Definition: Language.php:4792
Language\equals
equals(Language $lang)
Compare with an other language object.
Definition: Language.php:4409
Language\getSpecialPageAliases
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
Definition: Language.php:3220
Language\getMessagesFileName
static getMessagesFileName( $code)
Definition: Language.php:4502
Language\getPluralRuleTypes
getPluralRuleTypes()
Get the plural rule types for the language.
Definition: Language.php:4984
Language\capitalizeAllNouns
capitalizeAllNouns()
Definition: Language.php:3118
Language\specialList
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
Definition: Language.php:4853
Language\$mHebrewCalendarMonthGenMsgs
static $mHebrewCalendarMonthGenMsgs
Definition: Language.php:103
$wgMetaNamespace
$wgMetaNamespace
Name of the project namespace.
Definition: DefaultSettings.php:3851
true
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
Definition: hooks.txt:2006
Language\checkTitleEncoding
checkTitleEncoding( $s)
Definition: Language.php:2811
Language\$mParentLanguage
$mParentLanguage
Definition: Language.php:43
php
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
Definition: injection.txt:37
Language\isWellFormedLanguageTag
static isWellFormedLanguageTag( $code, $lenient=false)
Returns true if a language code string is a well-formed language tag according to RFC 5646.
Definition: Language.php:284
Language\getFormattedNamespaces
getFormattedNamespaces()
A convenience function that returns getNamespaces() with spaces instead of underscores in values.
Definition: Language.php:524
Language\lcfirst
lcfirst( $str)
Definition: Language.php:2695
Language\initEncoding
initEncoding()
Definition: Language.php:2968
Language\formatExpiry
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
Definition: Language.php:4662
Language\truncateInternal
truncateInternal( $string, $length, $ellipsis='...', $adjustLength=true, $measureLength, $getSubstring)
Internal method used for truncation.
Definition: Language.php:3572
Language\isKnownLanguageTag
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
Definition: Language.php:385
Language\emphasize
emphasize( $text)
Italic is unsuitable for some languages.
Definition: Language.php:3239
Language\getFallbackLanguages
getFallbackLanguages()
Definition: Language.php:446
Language\getDurationIntervals
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
Definition: Language.php:2337
Language\caseFold
caseFold( $s)
Return a case-folded representation of $s.
Definition: Language.php:2802
Language\userTimeAndDate
userTimeAndDate( $ts, User $user, array $options=[])
Get the formatted date and time for the given timestamp and formatted for the given user.
Definition: Language.php:2473
Language\segmentForDiff
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
Definition: Language.php:4139
Language\classFromCode
static classFromCode( $code, $fallback=true)
Definition: Language.php:4474
MWNamespace\getCanonicalIndex
static getCanonicalIndex( $name)
Returns the index for a given canonical name, or NULL The input must be converted to lower case first...
Definition: MWNamespace.php:271
Language\getWeekdayAbbreviation
getWeekdayAbbreviation( $key)
Definition: Language.php:980
Language\msg
msg( $msg)
Get message object in this language.
Definition: Language.php:918
Language\needsGenderDistinction
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
Definition: Language.php:589
Language\getParsedTitle
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
Definition: Language.php:4320
Language\dateTimeObjFormat
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
Definition: Language.php:1024
FormatJson\decode
static decode( $value, $assoc=false)
Decodes a JSON string.
Definition: FormatJson.php:187
Language\getLocalisationCache
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:406
MWException
MediaWiki exception.
Definition: MWException.php:26
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:78
Language\viewPrevNext
viewPrevNext(Title $title, $offset, $limit, array $query=[], $atend=false)
Generate (prev x| next x) (20|50|100...) type links for paging.
Definition: Language.php:4875
Language\$transformData
$transformData
ReplacementArray object caches.
Definition: Language.php:55
Language\tsToIranian
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
Definition: Language.php:1586
Language\unsegmentForDiff
unsegmentForDiff( $text)
and unsegment to show the result
Definition: Language.php:4149
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=null, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:803
Language\autoConvert
autoConvert( $text, $variant=false)
convert text to a variant
Definition: Language.php:4171
Language\truncateHtml
truncateHtml( $text, $length, $ellipsis='...')
Truncate a string of valid HTML to a specified length in bytes, appending an optional string (e....
Definition: Language.php:3683
Language\normalize
normalize( $s)
Convert a UTF-8 string to normal form C.
Definition: Language.php:3001
Language\getBookstoreList
getBookstoreList()
Exports $wgBookstoreListEn.
Definition: Language.php:454
Language\$mHebrewCalendarMonthMsgs
static $mHebrewCalendarMonthMsgs
Definition: Language.php:95
Language\pipeList
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
Definition: Language.php:3479
Language\$grammarTransformations
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
Definition: Language.php:146
Language\doMagicHook
doMagicHook()
Run the LanguageGetMagic hook once.
Definition: Language.php:3166
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2812
Language\convertForSearchResult
convertForSearchResult( $termsArray)
Definition: Language.php:2901
wfUrlProtocolsWithoutProtRel
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
Definition: GlobalFunctions.php:772
Language\getMessageFor
static getMessageFor( $key, $code)
Get a message for a given language.
Definition: Language.php:4607
$matches
$matches
Definition: NoLocalSettings.php:24
Language\getGenderNsText
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
Definition: Language.php:574
Language\getHtmlCode
getHtmlCode()
Get the code in BCP 47 format which we can use inside of html lang="" tags.
Definition: Language.php:4435
Language\getNamespaceAliases
getNamespaceAliases()
Definition: Language.php:622
Language\getLocalNsIndex
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:613
Language\userDate
userDate( $ts, User $user, array $options=[])
Get the formatted date for the given timestamp and formatted for the given user.
Definition: Language.php:2427
MediaWiki
A helper class for throttling authentication attempts.
Language\addMagicWordsByLang
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
Definition: Language.php:3205
Language\getMonthName
getMonthName( $key)
Definition: Language.php:926
Language\$mMonthMsgs
static $mMonthMsgs
Definition: Language.php:73
Language\truncateForDatabase
truncateForDatabase( $string, $length, $ellipsis='...', $adjustLength=true)
Truncate a string to a specified length in bytes, appending an optional string (e....
Definition: Language.php:3522
Language\getAllMessages
getAllMessages()
Definition: Language.php:2605
Language\getMagic
getMagic( $mw)
Fill a MagicWord object with data from here.
Definition: Language.php:3179
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:34
Language\$rle
static $rle
Definition: Language.php:158
Language\isRTL
isRTL()
For right-to-left language support.
Definition: Language.php:3042
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3021
Language\userTime
userTime( $ts, User $user, array $options=[])
Get the formatted time for the given timestamp and formatted for the given user.
Definition: Language.php:2450
$wgLocalisationCacheConf
$wgLocalisationCacheConf
Localisation cache configuration.
Definition: DefaultSettings.php:2535
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
Language\getNsIndex
getNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:696
Language\getNamespaceIds
getNamespaceIds()
Definition: Language.php:666
Language\getPluralRuleIndexNumber
getPluralRuleIndexNumber( $number)
Find the index number of the plural rule appropriate for the given number.
Definition: Language.php:5003
Language\getMonthNameGen
getMonthNameGen( $key)
Definition: Language.php:945
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Language\convertDoubleWidth
static convertDoubleWidth( $string)
convert double-width roman characters to single-width.
Definition: Language.php:2871
Language\getCompiledPluralRules
getCompiledPluralRules()
Get the compiled plural rules for the language.
Definition: Language.php:4946
Language\getHumanTimestampInternal
getHumanTimestampInternal(MWTimestamp $ts, MWTimestamp $relativeTo, User $user)
Convert an MWTimestamp into a pretty human-readable timestamp using the given user preferences and re...
Definition: Language.php:2529
Language\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:4252
Language\digitGroupingPattern
digitGroupingPattern()
Definition: Language.php:3391
Language\ucwords
ucwords( $str)
Definition: Language.php:2738
Language\initContLang
initContLang()
Hook which will be called if this is the content language.
Definition: Language.php:439
Language\getPluralRules
getPluralRules()
Get the plural rules for the language.
Definition: Language.php:4965
list
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
Definition: deferred.txt:11
Language\getArrow
getArrow( $direction='forwards')
An arrow, depending on the language direction.
Definition: Language.php:3129
Language\uc
uc( $str, $first=false)
Convert a string to uppercase.
Definition: Language.php:2679
Language\$mVariants
$mVariants
Definition: Language.php:41
Language\translateBlockExpiry
translateBlockExpiry( $str, User $user=null, $now=0)
Definition: Language.php:4096
$options
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
Definition: hooks.txt:2001
Language\formatNumNoSeparators
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
Definition: Language.php:3293
$wgTranslateNumerals
$wgTranslateNumerals
For Hindi and Arabic use local numerals instead of Western style (0-9) numerals in interface.
Definition: DefaultSettings.php:3037
Language\$mHtmlCode
$mHtmlCode
Definition: Language.php:43
$wgNamespaceAliases
$wgNamespaceAliases
Namespace aliases.
Definition: DefaultSettings.php:3916
Language\$mLoaded
$mLoaded
Definition: Language.php:41
Language\isValidBuiltInCode
static isValidBuiltInCode( $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
Definition: Language.php:363
Language\formatDuration
formatDuration( $seconds, array $chosenIntervals=[])
Takes a number of seconds and turns it into a text using values such as hours and minutes.
Definition: Language.php:2311
Language\setNamespaces
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
Definition: Language.php:504
Language\linkPrefixCharset
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
Definition: Language.php:4371
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:77
ReplacementArray
Wrapper around strtr() that holds replacements.
Definition: ReplacementArray.php:24
Language\findVariantLink
findVariantLink(&$link, &$nt, $ignoreOtherCond=false)
If a language supports multiple variants, it is possible that non-existing link in one variant actual...
Definition: Language.php:4299
it
=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
Definition: contenthandler.txt:104
Language\fixVariableInNamespace
fixVariableInNamespace( $talk)
Definition: Language.php:4627
$value
$value
Definition: styleTest.css.php:45
Language\ucwordbreaks
ucwordbreaks( $str)
capitalize words at word breaks
Definition: Language.php:2762
Language\getNsText
getNsText( $index)
Get a namespace value by key.
Definition: Language.php:543
$wgLocalTZoffset
$wgLocalTZoffset
Set an offset from UTC in minutes to use for the default timezone setting for anonymous users and new...
Definition: DefaultSettings.php:3168
Language\getCode
getCode()
Get the internal language code for this language object.
Definition: Language.php:4421
Language\getPluralRuleType
getPluralRuleType( $number)
Find the plural rule type appropriate for the given number For example, if the language is set to Ara...
Definition: Language.php:5017
Language\dateFormat
dateFormat( $usePrefs=true)
This is meant to be used by time(), date(), and timeanddate() to get the date preference they're supp...
Definition: Language.php:2188
options
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
$wgMetaNamespaceTalk
$wgMetaNamespaceTalk
Name of the project talk namespace.
Definition: DefaultSettings.php:3860
Language\ucwordsCallbackMB
ucwordsCallbackMB( $matches)
Definition: Language.php:2648
SpecialBlock\getSuggestedDurations
static getSuggestedDurations( $lang=null)
Get an array of suggested block durations from MediaWiki:Ipboptions.
Definition: SpecialBlock.php:860
$wgLanguageCode
$wgLanguageCode
Site language code.
Definition: DefaultSettings.php:2887
Language\hebrewNumeral
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
Definition: Language.php:2023
wfIsInfinity
wfIsInfinity( $str)
Determine input string is represents as infinity.
Definition: GlobalFunctions.php:3321
Language\getFormattedNsText
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
Definition: Language.php:561
Languages
Definition: languages.inc:27
Language\getMessagesFor
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
Definition: Language.php:4595
Language\ucwordbreaksCallbackMB
ucwordbreaksCallbackMB( $matches)
Definition: Language.php:2640
NS_PROJECT_TALK
const NS_PROJECT_TALK
Definition: Defines.php:79
Language\handleExplicitPluralForms
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
Definition: Language.php:4025
LocalisationCache
Class for caching the contents of localisation files, Messages*.php and *.i18n.php.
Definition: LocalisationCache.php:39
Language\recodeInput
recodeInput( $s)
Definition: Language.php:2986
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:896
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:434
Language\getDirMark
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:3106
Language\normalizeForSearch
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
Definition: Language.php:2859
Language\isValidCode
static isValidCode( $code)
Returns true if a language code string is of a valid form, whether or not it exists.
Definition: Language.php:338
FakeConverter
A fake language variant converter.
Definition: FakeConverter.php:34
Language\userAdjust
userAdjust( $ts, $tz=false)
Used by date() and time() to adjust the time output.
Definition: Language.php:2110
Language\formatComputingNumbers
formatComputingNumbers( $size, $boundary, $messageKey)
Definition: Language.php:4802
Language\convertHtml
convertHtml( $text, $isTitle=false)
Perform output conversion on a string, and encode for safe HTML output.
Definition: Language.php:4244
Language\getFallbackFor
static getFallbackFor( $code)
Get the first fallback for a given language.
Definition: Language.php:4532
Language\__construct
__construct()
Definition: Language.php:415
Language\getMonthAbbreviationsArray
getMonthAbbreviationsArray()
Definition: Language.php:960
Language\getParentLanguage
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
Definition: Language.php:4382
Language\$dateFormatStrings
$dateFormatStrings
Definition: Language.php:45
Language\hasWordBreaks
hasWordBreaks()
Most writing systems use whitespace to break up words.
Definition: Language.php:2837
Language\getDir
getDir()
Return the correct HTML 'dir' attribute value for this language.
Definition: Language.php:3050
Language\date
date( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2253
Language\alignEnd
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
Definition: Language.php:3074
Title
Represents a title within MediaWiki.
Definition: Title.php:39
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Language\truncate_skip
truncate_skip(&$ret, $text, $search, $start, $len=null)
truncateHtml() helper function like strcspn() but adds the skipped chars to $ret
Definition: Language.php:3801
LanguageConverter\$languagesWithVariants
static array $languagesWithVariants
languages supporting variants
Definition: LanguageConverter.php:40
Language\setCode
setCode( $code)
Definition: Language.php:4445
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:934
Language\getDefaultDateFormat
getDefaultDateFormat()
Definition: Language.php:744
Language\getVariants
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
Definition: Language.php:4262
Language\convertPlural
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
Definition: Language.php:3995
$cache
$cache
Definition: mcc.php:33
other
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
$code
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
Definition: hooks.txt:865
Language\$mLangObjCache
static $mLangObjCache
Definition: Language.php:62
wfGetPrecompiledData
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
Definition: GlobalFunctions.php:2692
Language\getFallbacksIncludingSiteLanguage
static getFallbacksIncludingSiteLanguage( $code)
Get the ordered list of fallback languages, ending with the fallback language chain for the site lang...
Definition: Language.php:4564
$wgGrammarForms
$wgGrammarForms
Some languages need different word forms, usually for different cases.
Definition: DefaultSettings.php:2904
or
or
Definition: APACHE-LICENSE-2.0.txt:114
Language\getGrammarForms
getGrammarForms()
Get the grammar forms for the content language.
Definition: Language.php:3897
Language\removeBadCharFirst
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
Definition: Language.php:3657
Language\$strongDirRegex
static $strongDirRegex
Directionality test regex for embedBidi().
Definition: Language.php:174
as
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
Definition: distributors.txt:22
MWNamespace\getCanonicalNamespaces
static getCanonicalNamespaces( $rebuild=false)
Returns array of all defined namespaces with their canonical (English) names.
Definition: MWNamespace.php:230
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:94
$wgExtraGenderNamespaces
$wgExtraGenderNamespaces
Same as above, but for namespaces with gender distinction.
Definition: DefaultSettings.php:3896
Language\getHijriCalendarMonthName
getHijriCalendarMonthName( $key)
Definition: Language.php:1012
Language\getMessage
getMessage( $key)
Definition: Language.php:2598
Language\getGrammarTransformations
getGrammarTransformations()
Get the grammar transformations data for the language.
Definition: Language.php:3917
Language\getMessageKeysFor
static getMessageKeysFor( $code)
Get all message keys for a given language.
Definition: Language.php:4619
Language\getJsonMessagesFileName
static getJsonMessagesFileName( $code)
Definition: Language.php:4515
Language\firstChar
firstChar( $s)
Get the first character of a string.
Definition: Language.php:2913
NS_USER
const NS_USER
Definition: Defines.php:76
Language\getNamespaces
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
Definition: Language.php:464
Language\transformUsingPairFile
transformUsingPairFile( $file, $string)
Transform a string using serialized data stored in the given file (which must be in the serialized su...
Definition: Language.php:3026
utf8ToCodepoint
utf8ToCodepoint( $char)
Determine the Unicode codepoint of a single-character UTF-8 sequence.
Definition: UtfNormalUtil.php:88
Language\removeBadCharLast
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
Definition: Language.php:3631
Language\romanNumeral
static romanNumeral( $num)
Roman number formatting up to 10000.
Definition: Language.php:1992
Language\getConvRuleTitle
getConvRuleTitle()
Get the conversion rule title, if any.
Definition: Language.php:4937
of
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
Definition: globals.txt:10
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:183
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1137
Language\$namespaceAliases
$namespaceAliases
Definition: Language.php:50
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:56
Language\tsToHijri
static tsToHijri( $ts)
Converting Gregorian dates to Hijri dates.
Definition: Language.php:1646
Language\getHumanTimestamp
getHumanTimestamp(MWTimestamp $time, MWTimestamp $relativeTo=null, User $user=null)
Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
Definition: Language.php:2492
$t
$t
Definition: testCompression.php:69
Language\time
time( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2272
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
$minimumGroupingDigits
$minimumGroupingDigits
Definition: MessagesBg.php:221
Language\insertSpace
static insertSpace( $string, $pattern)
Definition: Language.php:2891
Language\isMultibyte
isMultibyte( $str)
Definition: Language.php:2730
Language\alignStart
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
Definition: Language.php:3062
Language\isSupportedLanguage
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
Definition: Language.php:256
Language\$mWeekdayMsgs
static $mWeekdayMsgs
Definition: Language.php:64
$query
null for the 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
Definition: hooks.txt:1620
Language\digitTransformTable
digitTransformTable()
Definition: Language.php:3398
Language\tsToYear
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
Definition: Language.php:1877
Language\getDirMarkEntity
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:3089
Language\strongDirFromContent
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
Definition: Language.php:1975
Language\hebrewYearStart
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
Definition: Language.php:1839
Language\$mNamespaceIds
$mNamespaceIds
Definition: Language.php:50
$IP
$IP
Definition: WebStart.php:52
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:53
Language\getDateFormatString
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
Definition: Language.php:2219
Language\$dataCache
static LocalisationCache $dataCache
Definition: Language.php:60
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
Language\getMagicWords
getMagicWords()
Get all magic words from cache.
Definition: Language.php:3159
Language\hasVariant
hasVariant( $variant)
Check if the language has the specific variant.
Definition: Language.php:4233
Language\numLink
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
Definition: Language.php:4921
Language\__destruct
__destruct()
Reduce memory usage.
Definition: Language.php:429
Language\commafy
commafy( $number)
Adds commas to a given number.
Definition: Language.php:3326
LanguageConverter
Base class for language conversion.
Definition: LanguageConverter.php:34
Language\$pdf
static $pdf
Definition: Language.php:159
$wgExtraNamespaces
$wgExtraNamespaces
Additional namespaces.
Definition: DefaultSettings.php:3888
Language
Internationalisation code.
Definition: Language.php:35
Language\newFromCode
static newFromCode( $code, $fallback=false)
Create a language object for a given language code.
Definition: Language.php:210
Language\$mExtendedSpecialPageAliases
$mExtendedSpecialPageAliases
Definition: Language.php:46
Language\$durationIntervals
static array $durationIntervals
Definition: Language.php:122
Language\tsToHebrew
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
Definition: Language.php:1698
Language\autoConvertToAllVariants
autoConvertToAllVariants( $text)
convert text to all supported variants
Definition: Language.php:4181
Language\linkTrail
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
Definition: Language.php:4361
$wgAllUnicodeFixes
$wgAllUnicodeFixes
Set this to always convert certain Unicode sequences to modern ones regardless of the content languag...
Definition: DefaultSettings.php:2994
Language\ucfirst
ucfirst( $str)
Make a string's first character uppercase.
Definition: Language.php:2659
Language\$mMagicExtensions
$mMagicExtensions
Definition: Language.php:42
$wgDummyLanguageCodes
$wgDummyLanguageCodes
Functionally the same as $wgExtraLanguageCodes, but deprecated.
Definition: DefaultSettings.php:2961
Language\getWeekdayName
getWeekdayName( $key)
Definition: Language.php:972
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2171
Language\gender
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
Definition: Language.php:3966
Language\$lre
static $lre
Unicode directional formatting characters, for embedBidi()
Definition: Language.php:157
Language\getHebrewCalendarMonthName
getHebrewCalendarMonthName( $key)
Definition: Language.php:996
Language\formatNum
formatNum( $number, $nocommafy=false)
Normally we output all numbers in plain en_US style, that is 293,291.235 for twohundredninetythreetho...
Definition: Language.php:3265
$type
$type
Definition: testCompression.php:48
Language\$fallbackLanguageCache
static array $fallbackLanguageCache
Cache for language fallbacks.
Definition: Language.php:140
Language\convertNamespace
convertNamespace( $ns, $variant=null)
Convert a namespace index to a string in the preferred variant.
Definition: Language.php:4213
Language\convertTitle
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
Definition: Language.php:4201