MediaWiki  1.32.0
Language.php
Go to the documentation of this file.
1 <?php
29 use CLDRPluralRuleParser\Evaluator;
30 
35 class Language {
40  const AS_AUTONYMS = null;
41 
46  const ALL = 'all';
47 
53  const SUPPORTED = 'mwfile';
54 
58  public $mConverter;
59 
60  public $mVariants, $mCode, $mLoaded = false;
61  public $mMagicExtensions = [], $mMagicHookDone = false;
62  private $mHtmlCode = null, $mParentLanguage = false;
63 
64  public $dateFormatStrings = [];
66 
68  protected $namespaceNames;
70 
74  public $transformData = [];
75 
79  static public $dataCache;
80 
81  static public $mLangObjCache = [];
82 
87  const MESSAGES_FALLBACKS = 0;
88 
93  const STRICT_FALLBACKS = 1;
94 
95  static public $mWeekdayMsgs = [
96  'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
97  'friday', 'saturday'
98  ];
99 
100  static public $mWeekdayAbbrevMsgs = [
101  'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
102  ];
103 
104  static public $mMonthMsgs = [
105  'january', 'february', 'march', 'april', 'may_long', 'june',
106  'july', 'august', 'september', 'october', 'november',
107  'december'
108  ];
109  static public $mMonthGenMsgs = [
110  'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
111  'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen',
112  'december-gen'
113  ];
114  static public $mMonthAbbrevMsgs = [
115  'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
116  'sep', 'oct', 'nov', 'dec'
117  ];
118 
119  static public $mIranianCalendarMonthMsgs = [
120  'iranian-calendar-m1', 'iranian-calendar-m2', 'iranian-calendar-m3',
121  'iranian-calendar-m4', 'iranian-calendar-m5', 'iranian-calendar-m6',
122  'iranian-calendar-m7', 'iranian-calendar-m8', 'iranian-calendar-m9',
123  'iranian-calendar-m10', 'iranian-calendar-m11', 'iranian-calendar-m12'
124  ];
125 
126  static public $mHebrewCalendarMonthMsgs = [
127  'hebrew-calendar-m1', 'hebrew-calendar-m2', 'hebrew-calendar-m3',
128  'hebrew-calendar-m4', 'hebrew-calendar-m5', 'hebrew-calendar-m6',
129  'hebrew-calendar-m7', 'hebrew-calendar-m8', 'hebrew-calendar-m9',
130  'hebrew-calendar-m10', 'hebrew-calendar-m11', 'hebrew-calendar-m12',
131  'hebrew-calendar-m6a', 'hebrew-calendar-m6b'
132  ];
133 
135  'hebrew-calendar-m1-gen', 'hebrew-calendar-m2-gen', 'hebrew-calendar-m3-gen',
136  'hebrew-calendar-m4-gen', 'hebrew-calendar-m5-gen', 'hebrew-calendar-m6-gen',
137  'hebrew-calendar-m7-gen', 'hebrew-calendar-m8-gen', 'hebrew-calendar-m9-gen',
138  'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen',
139  'hebrew-calendar-m6a-gen', 'hebrew-calendar-m6b-gen'
140  ];
141 
142  static public $mHijriCalendarMonthMsgs = [
143  'hijri-calendar-m1', 'hijri-calendar-m2', 'hijri-calendar-m3',
144  'hijri-calendar-m4', 'hijri-calendar-m5', 'hijri-calendar-m6',
145  'hijri-calendar-m7', 'hijri-calendar-m8', 'hijri-calendar-m9',
146  'hijri-calendar-m10', 'hijri-calendar-m11', 'hijri-calendar-m12'
147  ];
148 
153  static public $durationIntervals = [
154  'millennia' => 31556952000,
155  'centuries' => 3155695200,
156  'decades' => 315569520,
157  'years' => 31556952, // 86400 * ( 365 + ( 24 * 3 + 25 ) / 400 )
158  'weeks' => 604800,
159  'days' => 86400,
160  'hours' => 3600,
161  'minutes' => 60,
162  'seconds' => 1,
163  ];
164 
171  static private $fallbackLanguageCache = [];
172 
177  static private $grammarTransformations;
178 
183  static private $languageNameCache;
184 
188  static private $lre = "\u{202A}"; // U+202A LEFT-TO-RIGHT EMBEDDING
189  static private $rle = "\u{202B}"; // U+202B RIGHT-TO-LEFT EMBEDDING
190  static private $pdf = "\u{202C}"; // U+202C POP DIRECTIONAL FORMATTING
191 
203  // @codeCoverageIgnoreStart
204  // phpcs:ignore Generic.Files.LineLength
205  static private $strongDirRegex = '/(?:([\x{41}-\x{5a}\x{61}-\x{7a}\x{aa}\x{b5}\x{ba}\x{c0}-\x{d6}\x{d8}-\x{f6}\x{f8}-\x{2b8}\x{2bb}-\x{2c1}\x{2d0}\x{2d1}\x{2e0}-\x{2e4}\x{2ee}\x{370}-\x{373}\x{376}\x{377}\x{37a}-\x{37d}\x{37f}\x{386}\x{388}-\x{38a}\x{38c}\x{38e}-\x{3a1}\x{3a3}-\x{3f5}\x{3f7}-\x{482}\x{48a}-\x{52f}\x{531}-\x{556}\x{559}-\x{55f}\x{561}-\x{587}\x{589}\x{903}-\x{939}\x{93b}\x{93d}-\x{940}\x{949}-\x{94c}\x{94e}-\x{950}\x{958}-\x{961}\x{964}-\x{980}\x{982}\x{983}\x{985}-\x{98c}\x{98f}\x{990}\x{993}-\x{9a8}\x{9aa}-\x{9b0}\x{9b2}\x{9b6}-\x{9b9}\x{9bd}-\x{9c0}\x{9c7}\x{9c8}\x{9cb}\x{9cc}\x{9ce}\x{9d7}\x{9dc}\x{9dd}\x{9df}-\x{9e1}\x{9e6}-\x{9f1}\x{9f4}-\x{9fa}\x{a03}\x{a05}-\x{a0a}\x{a0f}\x{a10}\x{a13}-\x{a28}\x{a2a}-\x{a30}\x{a32}\x{a33}\x{a35}\x{a36}\x{a38}\x{a39}\x{a3e}-\x{a40}\x{a59}-\x{a5c}\x{a5e}\x{a66}-\x{a6f}\x{a72}-\x{a74}\x{a83}\x{a85}-\x{a8d}\x{a8f}-\x{a91}\x{a93}-\x{aa8}\x{aaa}-\x{ab0}\x{ab2}\x{ab3}\x{ab5}-\x{ab9}\x{abd}-\x{ac0}\x{ac9}\x{acb}\x{acc}\x{ad0}\x{ae0}\x{ae1}\x{ae6}-\x{af0}\x{af9}\x{b02}\x{b03}\x{b05}-\x{b0c}\x{b0f}\x{b10}\x{b13}-\x{b28}\x{b2a}-\x{b30}\x{b32}\x{b33}\x{b35}-\x{b39}\x{b3d}\x{b3e}\x{b40}\x{b47}\x{b48}\x{b4b}\x{b4c}\x{b57}\x{b5c}\x{b5d}\x{b5f}-\x{b61}\x{b66}-\x{b77}\x{b83}\x{b85}-\x{b8a}\x{b8e}-\x{b90}\x{b92}-\x{b95}\x{b99}\x{b9a}\x{b9c}\x{b9e}\x{b9f}\x{ba3}\x{ba4}\x{ba8}-\x{baa}\x{bae}-\x{bb9}\x{bbe}\x{bbf}\x{bc1}\x{bc2}\x{bc6}-\x{bc8}\x{bca}-\x{bcc}\x{bd0}\x{bd7}\x{be6}-\x{bf2}\x{c01}-\x{c03}\x{c05}-\x{c0c}\x{c0e}-\x{c10}\x{c12}-\x{c28}\x{c2a}-\x{c39}\x{c3d}\x{c41}-\x{c44}\x{c58}-\x{c5a}\x{c60}\x{c61}\x{c66}-\x{c6f}\x{c7f}\x{c82}\x{c83}\x{c85}-\x{c8c}\x{c8e}-\x{c90}\x{c92}-\x{ca8}\x{caa}-\x{cb3}\x{cb5}-\x{cb9}\x{cbd}-\x{cc4}\x{cc6}-\x{cc8}\x{cca}\x{ccb}\x{cd5}\x{cd6}\x{cde}\x{ce0}\x{ce1}\x{ce6}-\x{cef}\x{cf1}\x{cf2}\x{d02}\x{d03}\x{d05}-\x{d0c}\x{d0e}-\x{d10}\x{d12}-\x{d3a}\x{d3d}-\x{d40}\x{d46}-\x{d48}\x{d4a}-\x{d4c}\x{d4e}\x{d57}\x{d5f}-\x{d61}\x{d66}-\x{d75}\x{d79}-\x{d7f}\x{d82}\x{d83}\x{d85}-\x{d96}\x{d9a}-\x{db1}\x{db3}-\x{dbb}\x{dbd}\x{dc0}-\x{dc6}\x{dcf}-\x{dd1}\x{dd8}-\x{ddf}\x{de6}-\x{def}\x{df2}-\x{df4}\x{e01}-\x{e30}\x{e32}\x{e33}\x{e40}-\x{e46}\x{e4f}-\x{e5b}\x{e81}\x{e82}\x{e84}\x{e87}\x{e88}\x{e8a}\x{e8d}\x{e94}-\x{e97}\x{e99}-\x{e9f}\x{ea1}-\x{ea3}\x{ea5}\x{ea7}\x{eaa}\x{eab}\x{ead}-\x{eb0}\x{eb2}\x{eb3}\x{ebd}\x{ec0}-\x{ec4}\x{ec6}\x{ed0}-\x{ed9}\x{edc}-\x{edf}\x{f00}-\x{f17}\x{f1a}-\x{f34}\x{f36}\x{f38}\x{f3e}-\x{f47}\x{f49}-\x{f6c}\x{f7f}\x{f85}\x{f88}-\x{f8c}\x{fbe}-\x{fc5}\x{fc7}-\x{fcc}\x{fce}-\x{fda}\x{1000}-\x{102c}\x{1031}\x{1038}\x{103b}\x{103c}\x{103f}-\x{1057}\x{105a}-\x{105d}\x{1061}-\x{1070}\x{1075}-\x{1081}\x{1083}\x{1084}\x{1087}-\x{108c}\x{108e}-\x{109c}\x{109e}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1360}-\x{137c}\x{1380}-\x{138f}\x{13a0}-\x{13f5}\x{13f8}-\x{13fd}\x{1401}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16f8}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1735}\x{1736}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17b6}\x{17be}-\x{17c5}\x{17c7}\x{17c8}\x{17d4}-\x{17da}\x{17dc}\x{17e0}-\x{17e9}\x{1810}-\x{1819}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191e}\x{1923}-\x{1926}\x{1929}-\x{192b}\x{1930}\x{1931}\x{1933}-\x{1938}\x{1946}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19b0}-\x{19c9}\x{19d0}-\x{19da}\x{1a00}-\x{1a16}\x{1a19}\x{1a1a}\x{1a1e}-\x{1a55}\x{1a57}\x{1a61}\x{1a63}\x{1a64}\x{1a6d}-\x{1a72}\x{1a80}-\x{1a89}\x{1a90}-\x{1a99}\x{1aa0}-\x{1aad}\x{1b04}-\x{1b33}\x{1b35}\x{1b3b}\x{1b3d}-\x{1b41}\x{1b43}-\x{1b4b}\x{1b50}-\x{1b6a}\x{1b74}-\x{1b7c}\x{1b82}-\x{1ba1}\x{1ba6}\x{1ba7}\x{1baa}\x{1bae}-\x{1be5}\x{1be7}\x{1bea}-\x{1bec}\x{1bee}\x{1bf2}\x{1bf3}\x{1bfc}-\x{1c2b}\x{1c34}\x{1c35}\x{1c3b}-\x{1c49}\x{1c4d}-\x{1c7f}\x{1cc0}-\x{1cc7}\x{1cd3}\x{1ce1}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf3}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{200e}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{214f}\x{2160}-\x{2188}\x{2336}-\x{237a}\x{2395}\x{249c}-\x{24e9}\x{26ac}\x{2800}-\x{28ff}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d70}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{302e}\x{302f}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{3190}-\x{31ba}\x{31f0}-\x{321c}\x{3220}-\x{324f}\x{3260}-\x{327b}\x{327f}-\x{32b0}\x{32c0}-\x{32cb}\x{32d0}-\x{32fe}\x{3300}-\x{3376}\x{337b}-\x{33dd}\x{33e0}-\x{33fe}\x{3400}-\x{4db5}\x{4e00}-\x{9fd5}\x{a000}-\x{a48c}\x{a4d0}-\x{a60c}\x{a610}-\x{a62b}\x{a640}-\x{a66e}\x{a680}-\x{a69d}\x{a6a0}-\x{a6ef}\x{a6f2}-\x{a6f7}\x{a722}-\x{a787}\x{a789}-\x{a7ad}\x{a7b0}-\x{a7b7}\x{a7f7}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a824}\x{a827}\x{a830}-\x{a837}\x{a840}-\x{a873}\x{a880}-\x{a8c3}\x{a8ce}-\x{a8d9}\x{a8f2}-\x{a8fd}\x{a900}-\x{a925}\x{a92e}-\x{a946}\x{a952}\x{a953}\x{a95f}-\x{a97c}\x{a983}-\x{a9b2}\x{a9b4}\x{a9b5}\x{a9ba}\x{a9bb}\x{a9bd}-\x{a9cd}\x{a9cf}-\x{a9d9}\x{a9de}-\x{a9e4}\x{a9e6}-\x{a9fe}\x{aa00}-\x{aa28}\x{aa2f}\x{aa30}\x{aa33}\x{aa34}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa4d}\x{aa50}-\x{aa59}\x{aa5c}-\x{aa7b}\x{aa7d}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aaeb}\x{aaee}-\x{aaf5}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{ab30}-\x{ab65}\x{ab70}-\x{abe4}\x{abe6}\x{abe7}\x{abe9}-\x{abec}\x{abf0}-\x{abf9}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{e000}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}\x{10000}-\x{1000b}\x{1000d}-\x{10026}\x{10028}-\x{1003a}\x{1003c}\x{1003d}\x{1003f}-\x{1004d}\x{10050}-\x{1005d}\x{10080}-\x{100fa}\x{10100}\x{10102}\x{10107}-\x{10133}\x{10137}-\x{1013f}\x{101d0}-\x{101fc}\x{10280}-\x{1029c}\x{102a0}-\x{102d0}\x{10300}-\x{10323}\x{10330}-\x{1034a}\x{10350}-\x{10375}\x{10380}-\x{1039d}\x{1039f}-\x{103c3}\x{103c8}-\x{103d5}\x{10400}-\x{1049d}\x{104a0}-\x{104a9}\x{10500}-\x{10527}\x{10530}-\x{10563}\x{1056f}\x{10600}-\x{10736}\x{10740}-\x{10755}\x{10760}-\x{10767}\x{11000}\x{11002}-\x{11037}\x{11047}-\x{1104d}\x{11066}-\x{1106f}\x{11082}-\x{110b2}\x{110b7}\x{110b8}\x{110bb}-\x{110c1}\x{110d0}-\x{110e8}\x{110f0}-\x{110f9}\x{11103}-\x{11126}\x{1112c}\x{11136}-\x{11143}\x{11150}-\x{11172}\x{11174}-\x{11176}\x{11182}-\x{111b5}\x{111bf}-\x{111c9}\x{111cd}\x{111d0}-\x{111df}\x{111e1}-\x{111f4}\x{11200}-\x{11211}\x{11213}-\x{1122e}\x{11232}\x{11233}\x{11235}\x{11238}-\x{1123d}\x{11280}-\x{11286}\x{11288}\x{1128a}-\x{1128d}\x{1128f}-\x{1129d}\x{1129f}-\x{112a9}\x{112b0}-\x{112de}\x{112e0}-\x{112e2}\x{112f0}-\x{112f9}\x{11302}\x{11303}\x{11305}-\x{1130c}\x{1130f}\x{11310}\x{11313}-\x{11328}\x{1132a}-\x{11330}\x{11332}\x{11333}\x{11335}-\x{11339}\x{1133d}-\x{1133f}\x{11341}-\x{11344}\x{11347}\x{11348}\x{1134b}-\x{1134d}\x{11350}\x{11357}\x{1135d}-\x{11363}\x{11480}-\x{114b2}\x{114b9}\x{114bb}-\x{114be}\x{114c1}\x{114c4}-\x{114c7}\x{114d0}-\x{114d9}\x{11580}-\x{115b1}\x{115b8}-\x{115bb}\x{115be}\x{115c1}-\x{115db}\x{11600}-\x{11632}\x{1163b}\x{1163c}\x{1163e}\x{11641}-\x{11644}\x{11650}-\x{11659}\x{11680}-\x{116aa}\x{116ac}\x{116ae}\x{116af}\x{116b6}\x{116c0}-\x{116c9}\x{11700}-\x{11719}\x{11720}\x{11721}\x{11726}\x{11730}-\x{1173f}\x{118a0}-\x{118f2}\x{118ff}\x{11ac0}-\x{11af8}\x{12000}-\x{12399}\x{12400}-\x{1246e}\x{12470}-\x{12474}\x{12480}-\x{12543}\x{13000}-\x{1342e}\x{14400}-\x{14646}\x{16800}-\x{16a38}\x{16a40}-\x{16a5e}\x{16a60}-\x{16a69}\x{16a6e}\x{16a6f}\x{16ad0}-\x{16aed}\x{16af5}\x{16b00}-\x{16b2f}\x{16b37}-\x{16b45}\x{16b50}-\x{16b59}\x{16b5b}-\x{16b61}\x{16b63}-\x{16b77}\x{16b7d}-\x{16b8f}\x{16f00}-\x{16f44}\x{16f50}-\x{16f7e}\x{16f93}-\x{16f9f}\x{1b000}\x{1b001}\x{1bc00}-\x{1bc6a}\x{1bc70}-\x{1bc7c}\x{1bc80}-\x{1bc88}\x{1bc90}-\x{1bc99}\x{1bc9c}\x{1bc9f}\x{1d000}-\x{1d0f5}\x{1d100}-\x{1d126}\x{1d129}-\x{1d166}\x{1d16a}-\x{1d172}\x{1d183}\x{1d184}\x{1d18c}-\x{1d1a9}\x{1d1ae}-\x{1d1e8}\x{1d360}-\x{1d371}\x{1d400}-\x{1d454}\x{1d456}-\x{1d49c}\x{1d49e}\x{1d49f}\x{1d4a2}\x{1d4a5}\x{1d4a6}\x{1d4a9}-\x{1d4ac}\x{1d4ae}-\x{1d4b9}\x{1d4bb}\x{1d4bd}-\x{1d4c3}\x{1d4c5}-\x{1d505}\x{1d507}-\x{1d50a}\x{1d50d}-\x{1d514}\x{1d516}-\x{1d51c}\x{1d51e}-\x{1d539}\x{1d53b}-\x{1d53e}\x{1d540}-\x{1d544}\x{1d546}\x{1d54a}-\x{1d550}\x{1d552}-\x{1d6a5}\x{1d6a8}-\x{1d6da}\x{1d6dc}-\x{1d714}\x{1d716}-\x{1d74e}\x{1d750}-\x{1d788}\x{1d78a}-\x{1d7c2}\x{1d7c4}-\x{1d7cb}\x{1d800}-\x{1d9ff}\x{1da37}-\x{1da3a}\x{1da6d}-\x{1da74}\x{1da76}-\x{1da83}\x{1da85}-\x{1da8b}\x{1f110}-\x{1f12e}\x{1f130}-\x{1f169}\x{1f170}-\x{1f19a}\x{1f1e6}-\x{1f202}\x{1f210}-\x{1f23a}\x{1f240}-\x{1f248}\x{1f250}\x{1f251}\x{20000}-\x{2a6d6}\x{2a700}-\x{2b734}\x{2b740}-\x{2b81d}\x{2b820}-\x{2cea1}\x{2f800}-\x{2fa1d}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}])|([\x{590}\x{5be}\x{5c0}\x{5c3}\x{5c6}\x{5c8}-\x{5ff}\x{7c0}-\x{7ea}\x{7f4}\x{7f5}\x{7fa}-\x{815}\x{81a}\x{824}\x{828}\x{82e}-\x{858}\x{85c}-\x{89f}\x{200f}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb4f}\x{10800}-\x{1091e}\x{10920}-\x{10a00}\x{10a04}\x{10a07}-\x{10a0b}\x{10a10}-\x{10a37}\x{10a3b}-\x{10a3e}\x{10a40}-\x{10ae4}\x{10ae7}-\x{10b38}\x{10b40}-\x{10e5f}\x{10e7f}-\x{10fff}\x{1e800}-\x{1e8cf}\x{1e8d7}-\x{1edff}\x{1ef00}-\x{1efff}\x{608}\x{60b}\x{60d}\x{61b}-\x{64a}\x{66d}-\x{66f}\x{671}-\x{6d5}\x{6e5}\x{6e6}\x{6ee}\x{6ef}\x{6fa}-\x{710}\x{712}-\x{72f}\x{74b}-\x{7a5}\x{7b1}-\x{7bf}\x{8a0}-\x{8e2}\x{fb50}-\x{fd3d}\x{fd40}-\x{fdcf}\x{fdf0}-\x{fdfc}\x{fdfe}\x{fdff}\x{fe70}-\x{fefe}\x{1ee00}-\x{1eeef}\x{1eef2}-\x{1eeff}]))/u';
206  // @codeCoverageIgnoreEnd
207 
214  static function factory( $code ) {
216 
217  if ( isset( $wgDummyLanguageCodes[$code] ) ) {
219  }
220 
221  // get the language object to process
222  $langObj = self::$mLangObjCache[$code] ?? self::newFromCode( $code );
223 
224  // merge the language object in to get it up front in the cache
225  self::$mLangObjCache = array_merge( [ $code => $langObj ], self::$mLangObjCache );
226  // get rid of the oldest ones in case we have an overflow
227  self::$mLangObjCache = array_slice( self::$mLangObjCache, 0, $wgLangObjCacheSize, true );
228 
229  return $langObj;
230  }
231 
239  protected static function newFromCode( $code, $fallback = false ) {
240  if ( !self::isValidCode( $code ) ) {
241  throw new MWException( "Invalid language code \"$code\"" );
242  }
243 
244  if ( !self::isValidBuiltInCode( $code ) ) {
245  // It's not possible to customise this code with class files, so
246  // just return a Language object. This is to support uselang= hacks.
247  $lang = new Language;
248  $lang->setCode( $code );
249  return $lang;
250  }
251 
252  // Check if there is a language class for the code
253  $class = self::classFromCode( $code, $fallback );
254  // LanguageCode does not inherit Language
255  if ( class_exists( $class ) && is_a( $class, 'Language', true ) ) {
256  $lang = new $class;
257  return $lang;
258  }
259 
260  // Keep trying the fallback list until we find an existing class
261  $fallbacks = self::getFallbacksFor( $code );
262  foreach ( $fallbacks as $fallbackCode ) {
263  if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
264  throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
265  }
266 
267  $class = self::classFromCode( $fallbackCode );
268  if ( class_exists( $class ) ) {
269  $lang = new $class;
270  $lang->setCode( $code );
271  return $lang;
272  }
273  }
274 
275  throw new MWException( "Invalid fallback sequence for language '$code'" );
276  }
277 
283  public static function clearCaches() {
284  if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
285  throw new MWException( __METHOD__ . ' must not be used outside tests' );
286  }
287  self::$dataCache = null;
288  // Reinitialize $dataCache, since it's expected to always be available
290  self::$mLangObjCache = [];
291  self::$fallbackLanguageCache = [];
292  self::$grammarTransformations = null;
293  self::$languageNameCache = null;
294  }
295 
304  public static function isSupportedLanguage( $code ) {
305  if ( !self::isValidBuiltInCode( $code ) ) {
306  return false;
307  }
308 
309  if ( $code === 'qqq' ) {
310  return false;
311  }
312 
313  return is_readable( self::getMessagesFileName( $code ) ) ||
314  is_readable( self::getJsonMessagesFileName( $code ) );
315  }
316 
332  public static function isWellFormedLanguageTag( $code, $lenient = false ) {
333  $alpha = '[a-z]';
334  $digit = '[0-9]';
335  $alphanum = '[a-z0-9]';
336  $x = 'x'; # private use singleton
337  $singleton = '[a-wy-z]'; # other singleton
338  $s = $lenient ? '[-_]' : '-';
339 
340  $language = "$alpha{2,8}|$alpha{2,3}$s$alpha{3}";
341  $script = "$alpha{4}"; # ISO 15924
342  $region = "(?:$alpha{2}|$digit{3})"; # ISO 3166-1 alpha-2 or UN M.49
343  $variant = "(?:$alphanum{5,8}|$digit$alphanum{3})";
344  $extension = "$singleton(?:$s$alphanum{2,8})+";
345  $privateUse = "$x(?:$s$alphanum{1,8})+";
346 
347  # Define certain grandfathered codes, since otherwise the regex is pretty useless.
348  # Since these are limited, this is safe even later changes to the registry --
349  # the only oddity is that it might change the type of the tag, and thus
350  # the results from the capturing groups.
351  # https://www.iana.org/assignments/language-subtag-registry
352 
353  $grandfathered = "en{$s}GB{$s}oed"
354  . "|i{$s}(?:ami|bnn|default|enochian|hak|klingon|lux|mingo|navajo|pwn|tao|tay|tsu)"
355  . "|no{$s}(?:bok|nyn)"
356  . "|sgn{$s}(?:BE{$s}(?:fr|nl)|CH{$s}de)"
357  . "|zh{$s}min{$s}nan";
358 
359  $variantList = "$variant(?:$s$variant)*";
360  $extensionList = "$extension(?:$s$extension)*";
361 
362  $langtag = "(?:($language)"
363  . "(?:$s$script)?"
364  . "(?:$s$region)?"
365  . "(?:$s$variantList)?"
366  . "(?:$s$extensionList)?"
367  . "(?:$s$privateUse)?)";
368 
369  # The final breakdown, with capturing groups for each of these components
370  # The variants, extensions, grandfathered, and private-use may have interior '-'
371 
372  $root = "^(?:$langtag|$privateUse|$grandfathered)$";
373 
374  return (bool)preg_match( "/$root/", strtolower( $code ) );
375  }
376 
386  public static function isValidCode( $code ) {
387  static $cache = [];
388  if ( !isset( $cache[$code] ) ) {
389  // People think language codes are html safe, so enforce it.
390  // Ideally we should only allow a-zA-Z0-9-
391  // but, .+ and other chars are often used for {{int:}} hacks
392  // see bugs T39564, T39587, T38938
393  $cache[$code] =
394  // Protect against path traversal
395  strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
397  }
398  return $cache[$code];
399  }
400 
411  public static function isValidBuiltInCode( $code ) {
412  if ( !is_string( $code ) ) {
413  if ( is_object( $code ) ) {
414  $addmsg = " of class " . get_class( $code );
415  } else {
416  $addmsg = '';
417  }
418  $type = gettype( $code );
419  throw new MWException( __METHOD__ . " must be passed a string, $type given$addmsg" );
420  }
421 
422  return (bool)preg_match( '/^[a-z0-9-]{2,}$/', $code );
423  }
424 
433  public static function isKnownLanguageTag( $tag ) {
434  // Quick escape for invalid input to avoid exceptions down the line
435  // when code tries to process tags which are not valid at all.
436  if ( !self::isValidBuiltInCode( $tag ) ) {
437  return false;
438  }
439 
440  if ( isset( MediaWiki\Languages\Data\Names::$names[$tag] )
441  || self::fetchLanguageName( $tag, $tag ) !== ''
442  ) {
443  return true;
444  }
445 
446  return false;
447  }
448 
454  public static function getLocalisationCache() {
455  if ( is_null( self::$dataCache ) ) {
457  $class = $wgLocalisationCacheConf['class'];
458  self::$dataCache = new $class( $wgLocalisationCacheConf );
459  }
460  return self::$dataCache;
461  }
462 
463  function __construct() {
464  $this->mConverter = new FakeConverter( $this );
465  // Set the code to the name of the descendant
466  if ( static::class === 'Language' ) {
467  $this->mCode = 'en';
468  } else {
469  $this->mCode = str_replace( '_', '-', strtolower( substr( static::class, 8 ) ) );
470  }
472  }
473 
477  function __destruct() {
478  foreach ( $this as $name => $value ) {
479  unset( $this->$name );
480  }
481  }
482 
487  function initContLang() {
488  }
489 
494  public function getFallbackLanguages() {
495  return self::getFallbacksFor( $this->mCode );
496  }
497 
502  public function getBookstoreList() {
503  return self::$dataCache->getItem( $this->mCode, 'bookstoreList' );
504  }
505 
512  public function getNamespaces() {
513  if ( is_null( $this->namespaceNames ) ) {
515 
516  $validNamespaces = MWNamespace::getCanonicalNamespaces();
517 
518  $this->namespaceNames = $wgExtraNamespaces +
519  self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
520  $this->namespaceNames += $validNamespaces;
521 
522  $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
523  if ( $wgMetaNamespaceTalk ) {
524  $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
525  } else {
526  $talk = $this->namespaceNames[NS_PROJECT_TALK];
527  $this->namespaceNames[NS_PROJECT_TALK] =
528  $this->fixVariableInNamespace( $talk );
529  }
530 
531  # Sometimes a language will be localised but not actually exist on this wiki.
532  foreach ( $this->namespaceNames as $key => $text ) {
533  if ( !isset( $validNamespaces[$key] ) ) {
534  unset( $this->namespaceNames[$key] );
535  }
536  }
537 
538  # The above mixing may leave namespaces out of canonical order.
539  # Re-order by namespace ID number...
540  ksort( $this->namespaceNames );
541 
542  Hooks::run( 'LanguageGetNamespaces', [ &$this->namespaceNames ] );
543  }
544 
545  return $this->namespaceNames;
546  }
547 
552  public function setNamespaces( array $namespaces ) {
553  $this->namespaceNames = $namespaces;
554  $this->mNamespaceIds = null;
555  }
556 
560  public function resetNamespaces() {
561  $this->namespaceNames = null;
562  $this->mNamespaceIds = null;
563  $this->namespaceAliases = null;
564  }
565 
572  public function getFormattedNamespaces() {
573  $ns = $this->getNamespaces();
574  foreach ( $ns as $k => $v ) {
575  $ns[$k] = strtr( $v, '_', ' ' );
576  }
577  return $ns;
578  }
579 
591  public function getNsText( $index ) {
592  $ns = $this->getNamespaces();
593  return $ns[$index] ?? false;
594  }
595 
609  public function getFormattedNsText( $index ) {
610  $ns = $this->getNsText( $index );
611  return strtr( $ns, '_', ' ' );
612  }
613 
622  public function getGenderNsText( $index, $gender ) {
624 
626  (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
627 
628  return $ns[$index][$gender] ?? $this->getNsText( $index );
629  }
630 
637  public function needsGenderDistinction() {
639  if ( count( $wgExtraGenderNamespaces ) > 0 ) {
640  // $wgExtraGenderNamespaces overrides everything
641  return true;
642  } elseif ( isset( $wgExtraNamespaces[NS_USER] ) && isset( $wgExtraNamespaces[NS_USER_TALK] ) ) {
644  // $wgExtraNamespaces overrides any gender aliases specified in i18n files
645  return false;
646  } else {
647  // Check what is in i18n files
648  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
649  return count( $aliases ) > 0;
650  }
651  }
652 
661  function getLocalNsIndex( $text ) {
662  $lctext = $this->lc( $text );
663  $ids = $this->getNamespaceIds();
664  return $ids[$lctext] ?? false;
665  }
666 
670  public function getNamespaceAliases() {
671  if ( is_null( $this->namespaceAliases ) ) {
672  $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' );
673  if ( !$aliases ) {
674  $aliases = [];
675  } else {
676  foreach ( $aliases as $name => $index ) {
677  if ( $index === NS_PROJECT_TALK ) {
678  unset( $aliases[$name] );
679  $name = $this->fixVariableInNamespace( $name );
680  $aliases[$name] = $index;
681  }
682  }
683  }
684 
686  $genders = $wgExtraGenderNamespaces +
687  (array)self::$dataCache->getItem( $this->mCode, 'namespaceGenderAliases' );
688  foreach ( $genders as $index => $forms ) {
689  foreach ( $forms as $alias ) {
690  $aliases[$alias] = $index;
691  }
692  }
693 
694  # Also add converted namespace names as aliases, to avoid confusion.
695  $convertedNames = [];
696  foreach ( $this->getVariants() as $variant ) {
697  if ( $variant === $this->mCode ) {
698  continue;
699  }
700  foreach ( $this->getNamespaces() as $ns => $_ ) {
701  $convertedNames[$this->getConverter()->convertNamespace( $ns, $variant )] = $ns;
702  }
703  }
704 
705  $this->namespaceAliases = $aliases + $convertedNames;
706 
707  # Filter out aliases to namespaces that don't exist, e.g. from extensions
708  # that aren't loaded here but are included in the l10n cache.
709  # (array_intersect preserves keys from its first argument)
710  $this->namespaceAliases = array_intersect(
711  $this->namespaceAliases,
712  array_keys( $this->getNamespaces() )
713  );
714  }
715 
717  }
718 
722  public function getNamespaceIds() {
723  if ( is_null( $this->mNamespaceIds ) ) {
724  global $wgNamespaceAliases;
725  # Put namespace names and aliases into a hashtable.
726  # If this is too slow, then we should arrange it so that it is done
727  # before caching. The catch is that at pre-cache time, the above
728  # class-specific fixup hasn't been done.
729  $this->mNamespaceIds = [];
730  foreach ( $this->getNamespaces() as $index => $name ) {
731  $this->mNamespaceIds[$this->lc( $name )] = $index;
732  }
733  foreach ( $this->getNamespaceAliases() as $name => $index ) {
734  $this->mNamespaceIds[$this->lc( $name )] = $index;
735  }
736  if ( $wgNamespaceAliases ) {
737  foreach ( $wgNamespaceAliases as $name => $index ) {
738  $this->mNamespaceIds[$this->lc( $name )] = $index;
739  }
740  }
741  }
742  return $this->mNamespaceIds;
743  }
744 
752  public function getNsIndex( $text ) {
753  $lctext = $this->lc( $text );
754  $ns = MWNamespace::getCanonicalIndex( $lctext );
755  if ( $ns !== null ) {
756  return $ns;
757  }
758  $ids = $this->getNamespaceIds();
759  return $ids[$lctext] ?? false;
760  }
761 
769  public function getVariantname( $code, $usemsg = true ) {
770  $msg = "variantname-$code";
771  if ( $usemsg && wfMessage( $msg )->exists() ) {
772  return $this->getMessageFromDB( $msg );
773  }
775  if ( $name ) {
776  return $name; # if it's defined as a language name, show that
777  } else {
778  # otherwise, output the language code
779  return $code;
780  }
781  }
782 
786  public function getDatePreferences() {
787  return self::$dataCache->getItem( $this->mCode, 'datePreferences' );
788  }
789 
793  function getDateFormats() {
794  return self::$dataCache->getItem( $this->mCode, 'dateFormats' );
795  }
796 
800  public function getDefaultDateFormat() {
801  $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat' );
802  if ( $df === 'dmy or mdy' ) {
803  global $wgAmericanDates;
804  return $wgAmericanDates ? 'mdy' : 'dmy';
805  } else {
806  return $df;
807  }
808  }
809 
813  public function getDatePreferenceMigrationMap() {
814  return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' );
815  }
816 
820  public function getExtraUserToggles() {
821  return (array)self::$dataCache->getItem( $this->mCode, 'extraUserToggles' );
822  }
823 
828  function getUserToggle( $tog ) {
829  return $this->getMessageFromDB( "tog-$tog" );
830  }
831 
843  public static function fetchLanguageNames( $inLanguage = self::AS_AUTONYMS, $include = 'mw' ) {
844  $cacheKey = $inLanguage === self::AS_AUTONYMS ? 'null' : $inLanguage;
845  $cacheKey .= ":$include";
846  if ( self::$languageNameCache === null ) {
847  self::$languageNameCache = new HashBagOStuff( [ 'maxKeys' => 20 ] );
848  }
849 
850  $ret = self::$languageNameCache->get( $cacheKey );
851  if ( !$ret ) {
852  $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
853  self::$languageNameCache->set( $cacheKey, $ret );
854  }
855  return $ret;
856  }
857 
868  private static function fetchLanguageNamesUncached(
869  $inLanguage = self::AS_AUTONYMS,
870  $include = 'mw'
871  ) {
872  global $wgExtraLanguageNames, $wgUsePigLatinVariant;
873 
874  // If passed an invalid language code to use, fallback to en
875  if ( $inLanguage !== self::AS_AUTONYMS && !self::isValidCode( $inLanguage ) ) {
876  $inLanguage = 'en';
877  }
878 
879  $names = [];
880 
881  if ( $inLanguage ) {
882  # TODO: also include when $inLanguage is null, when this code is more efficient
883  Hooks::run( 'LanguageGetTranslatedLanguageNames', [ &$names, $inLanguage ] );
884  }
885 
886  $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
887  if ( $wgUsePigLatinVariant ) {
888  // Pig Latin (for variant development)
889  $mwNames['en-x-piglatin'] = 'Igpay Atinlay';
890  }
891 
892  foreach ( $mwNames as $mwCode => $mwName ) {
893  # - Prefer own MediaWiki native name when not using the hook
894  # - For other names just add if not added through the hook
895  if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
896  $names[$mwCode] = $mwName;
897  }
898  }
899 
900  if ( $include === self::ALL ) {
901  ksort( $names );
902  return $names;
903  }
904 
905  $returnMw = [];
906  $coreCodes = array_keys( $mwNames );
907  foreach ( $coreCodes as $coreCode ) {
908  $returnMw[$coreCode] = $names[$coreCode];
909  }
910 
911  if ( $include === self::SUPPORTED ) {
912  $namesMwFile = [];
913  # We do this using a foreach over the codes instead of a directory
914  # loop so that messages files in extensions will work correctly.
915  foreach ( $returnMw as $code => $value ) {
916  if ( is_readable( self::getMessagesFileName( $code ) )
917  || is_readable( self::getJsonMessagesFileName( $code ) )
918  ) {
919  $namesMwFile[$code] = $names[$code];
920  }
921  }
922 
923  ksort( $namesMwFile );
924  return $namesMwFile;
925  }
926 
927  ksort( $returnMw );
928  # 'mw' option; default if it's not one of the other two options (all/mwfile)
929  return $returnMw;
930  }
931 
940  public static function fetchLanguageName(
941  $code,
942  $inLanguage = self::AS_AUTONYMS,
943  $include = self::ALL
944  ) {
945  $code = strtolower( $code );
946  $array = self::fetchLanguageNames( $inLanguage, $include );
947  return !array_key_exists( $code, $array ) ? '' : $array[$code];
948  }
949 
956  public function getMessageFromDB( $msg ) {
957  return $this->msg( $msg )->text();
958  }
959 
966  protected function msg( $msg ) {
967  return wfMessage( $msg )->inLanguage( $this );
968  }
969 
974  public function getMonthName( $key ) {
975  return $this->getMessageFromDB( self::$mMonthMsgs[$key - 1] );
976  }
977 
981  public function getMonthNamesArray() {
982  $monthNames = [ '' ];
983  for ( $i = 1; $i < 13; $i++ ) {
984  $monthNames[] = $this->getMonthName( $i );
985  }
986  return $monthNames;
987  }
988 
993  public function getMonthNameGen( $key ) {
994  return $this->getMessageFromDB( self::$mMonthGenMsgs[$key - 1] );
995  }
996 
1001  public function getMonthAbbreviation( $key ) {
1002  return $this->getMessageFromDB( self::$mMonthAbbrevMsgs[$key - 1] );
1003  }
1004 
1008  public function getMonthAbbreviationsArray() {
1009  $monthNames = [ '' ];
1010  for ( $i = 1; $i < 13; $i++ ) {
1011  $monthNames[] = $this->getMonthAbbreviation( $i );
1012  }
1013  return $monthNames;
1014  }
1015 
1020  public function getWeekdayName( $key ) {
1021  return $this->getMessageFromDB( self::$mWeekdayMsgs[$key - 1] );
1022  }
1023 
1028  function getWeekdayAbbreviation( $key ) {
1029  return $this->getMessageFromDB( self::$mWeekdayAbbrevMsgs[$key - 1] );
1030  }
1031 
1036  function getIranianCalendarMonthName( $key ) {
1037  return $this->getMessageFromDB( self::$mIranianCalendarMonthMsgs[$key - 1] );
1038  }
1039 
1044  function getHebrewCalendarMonthName( $key ) {
1045  return $this->getMessageFromDB( self::$mHebrewCalendarMonthMsgs[$key - 1] );
1046  }
1047 
1053  return $this->getMessageFromDB( self::$mHebrewCalendarMonthGenMsgs[$key - 1] );
1054  }
1055 
1060  function getHijriCalendarMonthName( $key ) {
1061  return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
1062  }
1063 
1072  private static function dateTimeObjFormat( &$dateTimeObj, $ts, $zone, $code ) {
1073  if ( !$dateTimeObj ) {
1074  $dateTimeObj = DateTime::createFromFormat(
1075  'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
1076  );
1077  }
1078  return $dateTimeObj->format( $code );
1079  }
1080 
1150  public function sprintfDate( $format, $ts, DateTimeZone $zone = null, &$ttl = 'unused' ) {
1151  $s = '';
1152  $raw = false;
1153  $roman = false;
1154  $hebrewNum = false;
1155  $dateTimeObj = false;
1156  $rawToggle = false;
1157  $iranian = false;
1158  $hebrew = false;
1159  $hijri = false;
1160  $thai = false;
1161  $minguo = false;
1162  $tenno = false;
1163 
1164  $usedSecond = false;
1165  $usedMinute = false;
1166  $usedHour = false;
1167  $usedAMPM = false;
1168  $usedDay = false;
1169  $usedWeek = false;
1170  $usedMonth = false;
1171  $usedYear = false;
1172  $usedISOYear = false;
1173  $usedIsLeapYear = false;
1174 
1175  $usedHebrewMonth = false;
1176  $usedIranianMonth = false;
1177  $usedHijriMonth = false;
1178  $usedHebrewYear = false;
1179  $usedIranianYear = false;
1180  $usedHijriYear = false;
1181  $usedTennoYear = false;
1182 
1183  if ( strlen( $ts ) !== 14 ) {
1184  throw new MWException( __METHOD__ . ": The timestamp $ts should have 14 characters" );
1185  }
1186 
1187  if ( !ctype_digit( $ts ) ) {
1188  throw new MWException( __METHOD__ . ": The timestamp $ts should be a number" );
1189  }
1190 
1191  $formatLength = strlen( $format );
1192  for ( $p = 0; $p < $formatLength; $p++ ) {
1193  $num = false;
1194  $code = $format[$p];
1195  if ( $code == 'x' && $p < $formatLength - 1 ) {
1196  $code .= $format[++$p];
1197  }
1198 
1199  if ( ( $code === 'xi'
1200  || $code === 'xj'
1201  || $code === 'xk'
1202  || $code === 'xm'
1203  || $code === 'xo'
1204  || $code === 'xt' )
1205  && $p < $formatLength - 1 ) {
1206  $code .= $format[++$p];
1207  }
1208 
1209  switch ( $code ) {
1210  case 'xx':
1211  $s .= 'x';
1212  break;
1213  case 'xn':
1214  $raw = true;
1215  break;
1216  case 'xN':
1217  $rawToggle = !$rawToggle;
1218  break;
1219  case 'xr':
1220  $roman = true;
1221  break;
1222  case 'xh':
1223  $hebrewNum = true;
1224  break;
1225  case 'xg':
1226  $usedMonth = true;
1227  $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
1228  break;
1229  case 'xjx':
1230  $usedHebrewMonth = true;
1231  if ( !$hebrew ) {
1232  $hebrew = self::tsToHebrew( $ts );
1233  }
1234  $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
1235  break;
1236  case 'd':
1237  $usedDay = true;
1238  $num = substr( $ts, 6, 2 );
1239  break;
1240  case 'D':
1241  $usedDay = true;
1242  $s .= $this->getWeekdayAbbreviation(
1243  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
1244  );
1245  break;
1246  case 'j':
1247  $usedDay = true;
1248  $num = intval( substr( $ts, 6, 2 ) );
1249  break;
1250  case 'xij':
1251  $usedDay = true;
1252  if ( !$iranian ) {
1253  $iranian = self::tsToIranian( $ts );
1254  }
1255  $num = $iranian[2];
1256  break;
1257  case 'xmj':
1258  $usedDay = true;
1259  if ( !$hijri ) {
1260  $hijri = self::tsToHijri( $ts );
1261  }
1262  $num = $hijri[2];
1263  break;
1264  case 'xjj':
1265  $usedDay = true;
1266  if ( !$hebrew ) {
1267  $hebrew = self::tsToHebrew( $ts );
1268  }
1269  $num = $hebrew[2];
1270  break;
1271  case 'l':
1272  $usedDay = true;
1273  $s .= $this->getWeekdayName(
1274  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
1275  );
1276  break;
1277  case 'F':
1278  $usedMonth = true;
1279  $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
1280  break;
1281  case 'xiF':
1282  $usedIranianMonth = true;
1283  if ( !$iranian ) {
1284  $iranian = self::tsToIranian( $ts );
1285  }
1286  $s .= $this->getIranianCalendarMonthName( $iranian[1] );
1287  break;
1288  case 'xmF':
1289  $usedHijriMonth = true;
1290  if ( !$hijri ) {
1291  $hijri = self::tsToHijri( $ts );
1292  }
1293  $s .= $this->getHijriCalendarMonthName( $hijri[1] );
1294  break;
1295  case 'xjF':
1296  $usedHebrewMonth = true;
1297  if ( !$hebrew ) {
1298  $hebrew = self::tsToHebrew( $ts );
1299  }
1300  $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
1301  break;
1302  case 'm':
1303  $usedMonth = true;
1304  $num = substr( $ts, 4, 2 );
1305  break;
1306  case 'M':
1307  $usedMonth = true;
1308  $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
1309  break;
1310  case 'n':
1311  $usedMonth = true;
1312  $num = intval( substr( $ts, 4, 2 ) );
1313  break;
1314  case 'xin':
1315  $usedIranianMonth = true;
1316  if ( !$iranian ) {
1317  $iranian = self::tsToIranian( $ts );
1318  }
1319  $num = $iranian[1];
1320  break;
1321  case 'xmn':
1322  $usedHijriMonth = true;
1323  if ( !$hijri ) {
1324  $hijri = self::tsToHijri( $ts );
1325  }
1326  $num = $hijri[1];
1327  break;
1328  case 'xjn':
1329  $usedHebrewMonth = true;
1330  if ( !$hebrew ) {
1331  $hebrew = self::tsToHebrew( $ts );
1332  }
1333  $num = $hebrew[1];
1334  break;
1335  case 'xjt':
1336  $usedHebrewMonth = true;
1337  if ( !$hebrew ) {
1338  $hebrew = self::tsToHebrew( $ts );
1339  }
1340  $num = $hebrew[3];
1341  break;
1342  case 'Y':
1343  $usedYear = true;
1344  $num = substr( $ts, 0, 4 );
1345  break;
1346  case 'xiY':
1347  $usedIranianYear = true;
1348  if ( !$iranian ) {
1349  $iranian = self::tsToIranian( $ts );
1350  }
1351  $num = $iranian[0];
1352  break;
1353  case 'xmY':
1354  $usedHijriYear = true;
1355  if ( !$hijri ) {
1356  $hijri = self::tsToHijri( $ts );
1357  }
1358  $num = $hijri[0];
1359  break;
1360  case 'xjY':
1361  $usedHebrewYear = true;
1362  if ( !$hebrew ) {
1363  $hebrew = self::tsToHebrew( $ts );
1364  }
1365  $num = $hebrew[0];
1366  break;
1367  case 'xkY':
1368  $usedYear = true;
1369  if ( !$thai ) {
1370  $thai = self::tsToYear( $ts, 'thai' );
1371  }
1372  $num = $thai[0];
1373  break;
1374  case 'xoY':
1375  $usedYear = true;
1376  if ( !$minguo ) {
1377  $minguo = self::tsToYear( $ts, 'minguo' );
1378  }
1379  $num = $minguo[0];
1380  break;
1381  case 'xtY':
1382  $usedTennoYear = true;
1383  if ( !$tenno ) {
1384  $tenno = self::tsToYear( $ts, 'tenno' );
1385  }
1386  $num = $tenno[0];
1387  break;
1388  case 'y':
1389  $usedYear = true;
1390  $num = substr( $ts, 2, 2 );
1391  break;
1392  case 'xiy':
1393  $usedIranianYear = true;
1394  if ( !$iranian ) {
1395  $iranian = self::tsToIranian( $ts );
1396  }
1397  $num = substr( $iranian[0], -2 );
1398  break;
1399  case 'xit':
1400  $usedIranianYear = true;
1401  if ( !$iranian ) {
1402  $iranian = self::tsToIranian( $ts );
1403  }
1404  $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
1405  break;
1406  case 'xiz':
1407  $usedIranianYear = true;
1408  if ( !$iranian ) {
1409  $iranian = self::tsToIranian( $ts );
1410  }
1411  $num = $iranian[3];
1412  break;
1413  case 'a':
1414  $usedAMPM = true;
1415  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
1416  break;
1417  case 'A':
1418  $usedAMPM = true;
1419  $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
1420  break;
1421  case 'g':
1422  $usedHour = true;
1423  $h = substr( $ts, 8, 2 );
1424  $num = $h % 12 ? $h % 12 : 12;
1425  break;
1426  case 'G':
1427  $usedHour = true;
1428  $num = intval( substr( $ts, 8, 2 ) );
1429  break;
1430  case 'h':
1431  $usedHour = true;
1432  $h = substr( $ts, 8, 2 );
1433  $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
1434  break;
1435  case 'H':
1436  $usedHour = true;
1437  $num = substr( $ts, 8, 2 );
1438  break;
1439  case 'i':
1440  $usedMinute = true;
1441  $num = substr( $ts, 10, 2 );
1442  break;
1443  case 's':
1444  $usedSecond = true;
1445  $num = substr( $ts, 12, 2 );
1446  break;
1447  case 'c':
1448  case 'r':
1449  $usedSecond = true;
1450  // fall through
1451  case 'e':
1452  case 'O':
1453  case 'P':
1454  case 'T':
1455  $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1456  break;
1457  case 'w':
1458  case 'N':
1459  case 'z':
1460  $usedDay = true;
1461  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1462  break;
1463  case 'W':
1464  $usedWeek = true;
1465  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1466  break;
1467  case 't':
1468  $usedMonth = true;
1469  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1470  break;
1471  case 'L':
1472  $usedIsLeapYear = true;
1473  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1474  break;
1475  case 'o':
1476  $usedISOYear = true;
1477  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1478  break;
1479  case 'U':
1480  $usedSecond = true;
1481  // fall through
1482  case 'I':
1483  case 'Z':
1484  $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
1485  break;
1486  case '\\':
1487  # Backslash escaping
1488  if ( $p < $formatLength - 1 ) {
1489  $s .= $format[++$p];
1490  } else {
1491  $s .= '\\';
1492  }
1493  break;
1494  case '"':
1495  # Quoted literal
1496  if ( $p < $formatLength - 1 ) {
1497  $endQuote = strpos( $format, '"', $p + 1 );
1498  if ( $endQuote === false ) {
1499  # No terminating quote, assume literal "
1500  $s .= '"';
1501  } else {
1502  $s .= substr( $format, $p + 1, $endQuote - $p - 1 );
1503  $p = $endQuote;
1504  }
1505  } else {
1506  # Quote at end of string, assume literal "
1507  $s .= '"';
1508  }
1509  break;
1510  default:
1511  $s .= $format[$p];
1512  }
1513  if ( $num !== false ) {
1514  if ( $rawToggle || $raw ) {
1515  $s .= $num;
1516  $raw = false;
1517  } elseif ( $roman ) {
1518  $s .= self::romanNumeral( $num );
1519  $roman = false;
1520  } elseif ( $hebrewNum ) {
1521  $s .= self::hebrewNumeral( $num );
1522  $hebrewNum = false;
1523  } else {
1524  $s .= $this->formatNum( $num, true );
1525  }
1526  }
1527  }
1528 
1529  if ( $ttl === 'unused' ) {
1530  // No need to calculate the TTL, the caller wont use it anyway.
1531  } elseif ( $usedSecond ) {
1532  $ttl = 1;
1533  } elseif ( $usedMinute ) {
1534  $ttl = 60 - substr( $ts, 12, 2 );
1535  } elseif ( $usedHour ) {
1536  $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1537  } elseif ( $usedAMPM ) {
1538  $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 -
1539  substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1540  } elseif (
1541  $usedDay ||
1542  $usedHebrewMonth ||
1543  $usedIranianMonth ||
1544  $usedHijriMonth ||
1545  $usedHebrewYear ||
1546  $usedIranianYear ||
1547  $usedHijriYear ||
1548  $usedTennoYear
1549  ) {
1550  // @todo Someone who understands the non-Gregorian calendars
1551  // should write proper logic for them so that they don't need purged every day.
1552  $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 -
1553  substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1554  } else {
1555  $possibleTtls = [];
1556  $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 -
1557  substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
1558  if ( $usedWeek ) {
1559  $possibleTtls[] =
1560  ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
1561  $timeRemainingInDay;
1562  } elseif ( $usedISOYear ) {
1563  // December 28th falls on the last ISO week of the year, every year.
1564  // The last ISO week of a year can be 52 or 53.
1565  $lastWeekOfISOYear = DateTime::createFromFormat(
1566  'Ymd',
1567  substr( $ts, 0, 4 ) . '1228',
1568  $zone ?: new DateTimeZone( 'UTC' )
1569  )->format( 'W' );
1570  $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
1571  $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
1572  $timeRemainingInWeek =
1573  ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400
1574  + $timeRemainingInDay;
1575  $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
1576  }
1577 
1578  if ( $usedMonth ) {
1579  $possibleTtls[] =
1580  ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
1581  substr( $ts, 6, 2 ) ) * 86400
1582  + $timeRemainingInDay;
1583  } elseif ( $usedYear ) {
1584  $possibleTtls[] =
1585  ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
1586  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
1587  + $timeRemainingInDay;
1588  } elseif ( $usedIsLeapYear ) {
1589  $year = substr( $ts, 0, 4 );
1590  $timeRemainingInYear =
1591  ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
1592  self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
1593  + $timeRemainingInDay;
1594  $mod = $year % 4;
1595  if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
1596  // this isn't a leap year. see when the next one starts
1597  $nextCandidate = $year - $mod + 4;
1598  if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
1599  $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 +
1600  $timeRemainingInYear;
1601  } else {
1602  $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 +
1603  $timeRemainingInYear;
1604  }
1605  } else {
1606  // this is a leap year, so the next year isn't
1607  $possibleTtls[] = $timeRemainingInYear;
1608  }
1609  }
1610 
1611  if ( $possibleTtls ) {
1612  $ttl = min( $possibleTtls );
1613  }
1614  }
1615 
1616  return $s;
1617  }
1618 
1619  private static $GREG_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
1620  private static $IRANIAN_DAYS = [ 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 ];
1621 
1634  private static function tsToIranian( $ts ) {
1635  $gy = substr( $ts, 0, 4 ) - 1600;
1636  $gm = substr( $ts, 4, 2 ) - 1;
1637  $gd = substr( $ts, 6, 2 ) - 1;
1638 
1639  # Days passed from the beginning (including leap years)
1640  $gDayNo = 365 * $gy
1641  + floor( ( $gy + 3 ) / 4 )
1642  - floor( ( $gy + 99 ) / 100 )
1643  + floor( ( $gy + 399 ) / 400 );
1644 
1645  // Add days of the past months of this year
1646  for ( $i = 0; $i < $gm; $i++ ) {
1647  $gDayNo += self::$GREG_DAYS[$i];
1648  }
1649 
1650  // Leap years
1651  if ( $gm > 1 && ( ( $gy % 4 === 0 && $gy % 100 !== 0 || ( $gy % 400 == 0 ) ) ) ) {
1652  $gDayNo++;
1653  }
1654 
1655  // Days passed in current month
1656  $gDayNo += (int)$gd;
1657 
1658  $jDayNo = $gDayNo - 79;
1659 
1660  $jNp = floor( $jDayNo / 12053 );
1661  $jDayNo %= 12053;
1662 
1663  $jy = 979 + 33 * $jNp + 4 * floor( $jDayNo / 1461 );
1664  $jDayNo %= 1461;
1665 
1666  if ( $jDayNo >= 366 ) {
1667  $jy += floor( ( $jDayNo - 1 ) / 365 );
1668  $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
1669  }
1670 
1671  $jz = $jDayNo;
1672 
1673  for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
1674  $jDayNo -= self::$IRANIAN_DAYS[$i];
1675  }
1676 
1677  $jm = $i + 1;
1678  $jd = $jDayNo + 1;
1679 
1680  return [ $jy, $jm, $jd, $jz ];
1681  }
1682 
1694  private static function tsToHijri( $ts ) {
1695  $year = substr( $ts, 0, 4 );
1696  $month = substr( $ts, 4, 2 );
1697  $day = substr( $ts, 6, 2 );
1698 
1699  $zyr = $year;
1700  $zd = $day;
1701  $zm = $month;
1702  $zy = $zyr;
1703 
1704  if (
1705  ( $zy > 1582 ) || ( ( $zy == 1582 ) && ( $zm > 10 ) ) ||
1706  ( ( $zy == 1582 ) && ( $zm == 10 ) && ( $zd > 14 ) )
1707  ) {
1708  $zjd = (int)( ( 1461 * ( $zy + 4800 + (int)( ( $zm - 14 ) / 12 ) ) ) / 4 ) +
1709  (int)( ( 367 * ( $zm - 2 - 12 * ( (int)( ( $zm - 14 ) / 12 ) ) ) ) / 12 ) -
1710  (int)( ( 3 * (int)( ( ( $zy + 4900 + (int)( ( $zm - 14 ) / 12 ) ) / 100 ) ) ) / 4 ) +
1711  $zd - 32075;
1712  } else {
1713  $zjd = 367 * $zy - (int)( ( 7 * ( $zy + 5001 + (int)( ( $zm - 9 ) / 7 ) ) ) / 4 ) +
1714  (int)( ( 275 * $zm ) / 9 ) + $zd + 1729777;
1715  }
1716 
1717  $zl = $zjd - 1948440 + 10632;
1718  $zn = (int)( ( $zl - 1 ) / 10631 );
1719  $zl = $zl - 10631 * $zn + 354;
1720  $zj = ( (int)( ( 10985 - $zl ) / 5316 ) ) * ( (int)( ( 50 * $zl ) / 17719 ) ) +
1721  ( (int)( $zl / 5670 ) ) * ( (int)( ( 43 * $zl ) / 15238 ) );
1722  $zl = $zl - ( (int)( ( 30 - $zj ) / 15 ) ) * ( (int)( ( 17719 * $zj ) / 50 ) ) -
1723  ( (int)( $zj / 16 ) ) * ( (int)( ( 15238 * $zj ) / 43 ) ) + 29;
1724  $zm = (int)( ( 24 * $zl ) / 709 );
1725  $zd = $zl - (int)( ( 709 * $zm ) / 24 );
1726  $zy = 30 * $zn + $zj - 30;
1727 
1728  return [ $zy, $zm, $zd ];
1729  }
1730 
1746  private static function tsToHebrew( $ts ) {
1747  # Parse date
1748  $year = substr( $ts, 0, 4 );
1749  $month = substr( $ts, 4, 2 );
1750  $day = substr( $ts, 6, 2 );
1751 
1752  # Calculate Hebrew year
1753  $hebrewYear = $year + 3760;
1754 
1755  # Month number when September = 1, August = 12
1756  $month += 4;
1757  if ( $month > 12 ) {
1758  # Next year
1759  $month -= 12;
1760  $year++;
1761  $hebrewYear++;
1762  }
1763 
1764  # Calculate day of year from 1 September
1765  $dayOfYear = $day;
1766  for ( $i = 1; $i < $month; $i++ ) {
1767  if ( $i == 6 ) {
1768  # February
1769  $dayOfYear += 28;
1770  # Check if the year is leap
1771  if ( $year % 400 == 0 || ( $year % 4 == 0 && $year % 100 > 0 ) ) {
1772  $dayOfYear++;
1773  }
1774  } elseif ( $i == 8 || $i == 10 || $i == 1 || $i == 3 ) {
1775  $dayOfYear += 30;
1776  } else {
1777  $dayOfYear += 31;
1778  }
1779  }
1780 
1781  # Calculate the start of the Hebrew year
1782  $start = self::hebrewYearStart( $hebrewYear );
1783 
1784  # Calculate next year's start
1785  if ( $dayOfYear <= $start ) {
1786  # Day is before the start of the year - it is the previous year
1787  # Next year's start
1788  $nextStart = $start;
1789  # Previous year
1790  $year--;
1791  $hebrewYear--;
1792  # Add days since previous year's 1 September
1793  $dayOfYear += 365;
1794  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1795  # Leap year
1796  $dayOfYear++;
1797  }
1798  # Start of the new (previous) year
1799  $start = self::hebrewYearStart( $hebrewYear );
1800  } else {
1801  # Next year's start
1802  $nextStart = self::hebrewYearStart( $hebrewYear + 1 );
1803  }
1804 
1805  # Calculate Hebrew day of year
1806  $hebrewDayOfYear = $dayOfYear - $start;
1807 
1808  # Difference between year's days
1809  $diff = $nextStart - $start;
1810  # Add 12 (or 13 for leap years) days to ignore the difference between
1811  # Hebrew and Gregorian year (353 at least vs. 365/6) - now the
1812  # difference is only about the year type
1813  if ( ( $year % 400 == 0 ) || ( $year % 100 != 0 && $year % 4 == 0 ) ) {
1814  $diff += 13;
1815  } else {
1816  $diff += 12;
1817  }
1818 
1819  # Check the year pattern, and is leap year
1820  # 0 means an incomplete year, 1 means a regular year, 2 means a complete year
1821  # This is mod 30, to work on both leap years (which add 30 days of Adar I)
1822  # and non-leap years
1823  $yearPattern = $diff % 30;
1824  # Check if leap year
1825  $isLeap = $diff >= 30;
1826 
1827  # Calculate day in the month from number of day in the Hebrew year
1828  # Don't check Adar - if the day is not in Adar, we will stop before;
1829  # if it is in Adar, we will use it to check if it is Adar I or Adar II
1830  $hebrewDay = $hebrewDayOfYear;
1831  $hebrewMonth = 1;
1832  $days = 0;
1833  while ( $hebrewMonth <= 12 ) {
1834  # Calculate days in this month
1835  if ( $isLeap && $hebrewMonth == 6 ) {
1836  # Adar in a leap year
1837  if ( $isLeap ) {
1838  # Leap year - has Adar I, with 30 days, and Adar II, with 29 days
1839  $days = 30;
1840  if ( $hebrewDay <= $days ) {
1841  # Day in Adar I
1842  $hebrewMonth = 13;
1843  } else {
1844  # Subtract the days of Adar I
1845  $hebrewDay -= $days;
1846  # Try Adar II
1847  $days = 29;
1848  if ( $hebrewDay <= $days ) {
1849  # Day in Adar II
1850  $hebrewMonth = 14;
1851  }
1852  }
1853  }
1854  } elseif ( $hebrewMonth == 2 && $yearPattern == 2 ) {
1855  # Cheshvan in a complete year (otherwise as the rule below)
1856  $days = 30;
1857  } elseif ( $hebrewMonth == 3 && $yearPattern == 0 ) {
1858  # Kislev in an incomplete year (otherwise as the rule below)
1859  $days = 29;
1860  } else {
1861  # Odd months have 30 days, even have 29
1862  $days = 30 - ( $hebrewMonth - 1 ) % 2;
1863  }
1864  if ( $hebrewDay <= $days ) {
1865  # In the current month
1866  break;
1867  } else {
1868  # Subtract the days of the current month
1869  $hebrewDay -= $days;
1870  # Try in the next month
1871  $hebrewMonth++;
1872  }
1873  }
1874 
1875  return [ $hebrewYear, $hebrewMonth, $hebrewDay, $days ];
1876  }
1877 
1887  private static function hebrewYearStart( $year ) {
1888  $a = intval( ( 12 * ( $year - 1 ) + 17 ) % 19 );
1889  $b = intval( ( $year - 1 ) % 4 );
1890  $m = 32.044093161144 + 1.5542417966212 * $a + $b / 4.0 - 0.0031777940220923 * ( $year - 1 );
1891  if ( $m < 0 ) {
1892  $m--;
1893  }
1894  $Mar = intval( $m );
1895  if ( $m < 0 ) {
1896  $m++;
1897  }
1898  $m -= $Mar;
1899 
1900  $c = intval( ( $Mar + 3 * ( $year - 1 ) + 5 * $b + 5 ) % 7 );
1901  if ( $c == 0 && $a > 11 && $m >= 0.89772376543210 ) {
1902  $Mar++;
1903  } elseif ( $c == 1 && $a > 6 && $m >= 0.63287037037037 ) {
1904  $Mar += 2;
1905  } elseif ( $c == 2 || $c == 4 || $c == 6 ) {
1906  $Mar++;
1907  }
1908 
1909  $Mar += intval( ( $year - 3761 ) / 100 ) - intval( ( $year - 3761 ) / 400 ) - 24;
1910  return $Mar;
1911  }
1912 
1925  private static function tsToYear( $ts, $cName ) {
1926  $gy = substr( $ts, 0, 4 );
1927  $gm = substr( $ts, 4, 2 );
1928  $gd = substr( $ts, 6, 2 );
1929 
1930  if ( !strcmp( $cName, 'thai' ) ) {
1931  # Thai solar dates
1932  # Add 543 years to the Gregorian calendar
1933  # Months and days are identical
1934  $gy_offset = $gy + 543;
1935  # fix for dates between 1912 and 1941
1936  # https://en.wikipedia.org/?oldid=836596673#New_year
1937  if ( $gy >= 1912 && $gy <= 1940 ) {
1938  if ( $gm <= 3 ) {
1939  $gy_offset--;
1940  }
1941  $gm = ( $gm - 3 ) % 12;
1942  }
1943  } elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) {
1944  # Minguo dates
1945  # Deduct 1911 years from the Gregorian calendar
1946  # Months and days are identical
1947  $gy_offset = $gy - 1911;
1948  } elseif ( !strcmp( $cName, 'tenno' ) ) {
1949  # Nengō dates up to Meiji period
1950  # Deduct years from the Gregorian calendar
1951  # depending on the nengo periods
1952  # Months and days are identical
1953  if ( ( $gy < 1912 )
1954  || ( ( $gy == 1912 ) && ( $gm < 7 ) )
1955  || ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd < 31 ) )
1956  ) {
1957  # Meiji period
1958  $gy_gannen = $gy - 1868 + 1;
1959  $gy_offset = $gy_gannen;
1960  if ( $gy_gannen == 1 ) {
1961  $gy_offset = '元';
1962  }
1963  $gy_offset = '明治' . $gy_offset;
1964  } elseif (
1965  ( ( $gy == 1912 ) && ( $gm == 7 ) && ( $gd == 31 ) ) ||
1966  ( ( $gy == 1912 ) && ( $gm >= 8 ) ) ||
1967  ( ( $gy > 1912 ) && ( $gy < 1926 ) ) ||
1968  ( ( $gy == 1926 ) && ( $gm < 12 ) ) ||
1969  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd < 26 ) )
1970  ) {
1971  # Taishō period
1972  $gy_gannen = $gy - 1912 + 1;
1973  $gy_offset = $gy_gannen;
1974  if ( $gy_gannen == 1 ) {
1975  $gy_offset = '元';
1976  }
1977  $gy_offset = '大正' . $gy_offset;
1978  } elseif (
1979  ( ( $gy == 1926 ) && ( $gm == 12 ) && ( $gd >= 26 ) ) ||
1980  ( ( $gy > 1926 ) && ( $gy < 1989 ) ) ||
1981  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd < 8 ) )
1982  ) {
1983  # Shōwa period
1984  $gy_gannen = $gy - 1926 + 1;
1985  $gy_offset = $gy_gannen;
1986  if ( $gy_gannen == 1 ) {
1987  $gy_offset = '元';
1988  }
1989  $gy_offset = '昭和' . $gy_offset;
1990  } else {
1991  # Heisei period
1992  $gy_gannen = $gy - 1989 + 1;
1993  $gy_offset = $gy_gannen;
1994  if ( $gy_gannen == 1 ) {
1995  $gy_offset = '元';
1996  }
1997  $gy_offset = '平成' . $gy_offset;
1998  }
1999  } else {
2000  $gy_offset = $gy;
2001  }
2002 
2003  return [ $gy_offset, $gm, $gd ];
2004  }
2005 
2019  private static function strongDirFromContent( $text = '' ) {
2020  if ( !preg_match( self::$strongDirRegex, $text, $matches ) ) {
2021  return null;
2022  }
2023  if ( $matches[1] === '' ) {
2024  return 'rtl';
2025  }
2026  return 'ltr';
2027  }
2028 
2036  static function romanNumeral( $num ) {
2037  static $table = [
2038  [ '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ],
2039  [ '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ],
2040  [ '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ],
2041  [ '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM',
2042  'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' ]
2043  ];
2044 
2045  $num = intval( $num );
2046  if ( $num > 10000 || $num <= 0 ) {
2047  return $num;
2048  }
2049 
2050  $s = '';
2051  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2052  if ( $num >= $pow10 ) {
2053  $s .= $table[$i][(int)floor( $num / $pow10 )];
2054  }
2055  $num = $num % $pow10;
2056  }
2057  return $s;
2058  }
2059 
2067  static function hebrewNumeral( $num ) {
2068  static $table = [
2069  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ],
2070  [ '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ],
2071  [ '',
2072  [ 'ק' ],
2073  [ 'ר' ],
2074  [ 'ש' ],
2075  [ 'ת' ],
2076  [ 'ת', 'ק' ],
2077  [ 'ת', 'ר' ],
2078  [ 'ת', 'ש' ],
2079  [ 'ת', 'ת' ],
2080  [ 'ת', 'ת', 'ק' ],
2081  [ 'ת', 'ת', 'ר' ],
2082  ],
2083  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ]
2084  ];
2085 
2086  $num = intval( $num );
2087  if ( $num > 9999 || $num <= 0 ) {
2088  return $num;
2089  }
2090 
2091  // Round thousands have special notations
2092  if ( $num === 1000 ) {
2093  return "א' אלף";
2094  } elseif ( $num % 1000 === 0 ) {
2095  return $table[0][$num / 1000] . "' אלפים";
2096  }
2097 
2098  $letters = [];
2099 
2100  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2101  if ( $num >= $pow10 ) {
2102  if ( $num === 15 || $num === 16 ) {
2103  $letters[] = $table[0][9];
2104  $letters[] = $table[0][$num - 9];
2105  $num = 0;
2106  } else {
2107  $letters = array_merge(
2108  $letters,
2109  (array)$table[$i][intval( $num / $pow10 )]
2110  );
2111 
2112  if ( $pow10 === 1000 ) {
2113  $letters[] = "'";
2114  }
2115  }
2116  }
2117 
2118  $num = $num % $pow10;
2119  }
2120 
2121  $preTransformLength = count( $letters );
2122  if ( $preTransformLength === 1 ) {
2123  // Add geresh (single quote) to one-letter numbers
2124  $letters[] = "'";
2125  } else {
2126  $lastIndex = $preTransformLength - 1;
2127  $letters[$lastIndex] = str_replace(
2128  [ 'כ', 'מ', 'נ', 'פ', 'צ' ],
2129  [ 'ך', 'ם', 'ן', 'ף', 'ץ' ],
2130  $letters[$lastIndex]
2131  );
2132 
2133  // Add gershayim (double quote) to multiple-letter numbers,
2134  // but exclude numbers with only one letter after the thousands
2135  // (1001-1009, 1020, 1030, 2001-2009, etc.)
2136  if ( $letters[1] === "'" && $preTransformLength === 3 ) {
2137  $letters[] = "'";
2138  } else {
2139  array_splice( $letters, -1, 0, '"' );
2140  }
2141  }
2142 
2143  return implode( $letters );
2144  }
2145 
2154  public function userAdjust( $ts, $tz = false ) {
2155  global $wgUser, $wgLocalTZoffset;
2156 
2157  if ( $tz === false ) {
2158  $tz = $wgUser->getOption( 'timecorrection' );
2159  }
2160 
2161  $data = explode( '|', $tz, 3 );
2162 
2163  if ( $data[0] == 'ZoneInfo' ) {
2164  try {
2165  $userTZ = new DateTimeZone( $data[2] );
2166  $date = new DateTime( $ts, new DateTimeZone( 'UTC' ) );
2167  $date->setTimezone( $userTZ );
2168  return $date->format( 'YmdHis' );
2169  } catch ( Exception $e ) {
2170  // Unrecognized timezone, default to 'Offset' with the stored offset.
2171  $data[0] = 'Offset';
2172  }
2173  }
2174 
2175  if ( $data[0] == 'System' || $tz == '' ) {
2176  # Global offset in minutes.
2177  $minDiff = $wgLocalTZoffset;
2178  } elseif ( $data[0] == 'Offset' ) {
2179  $minDiff = intval( $data[1] );
2180  } else {
2181  $data = explode( ':', $tz );
2182  if ( count( $data ) == 2 ) {
2183  $data[0] = intval( $data[0] );
2184  $data[1] = intval( $data[1] );
2185  $minDiff = abs( $data[0] ) * 60 + $data[1];
2186  if ( $data[0] < 0 ) {
2187  $minDiff = -$minDiff;
2188  }
2189  } else {
2190  $minDiff = intval( $data[0] ) * 60;
2191  }
2192  }
2193 
2194  # No difference ? Return time unchanged
2195  if ( 0 == $minDiff ) {
2196  return $ts;
2197  }
2198 
2199  Wikimedia\suppressWarnings(); // E_STRICT system time bitching
2200  # Generate an adjusted date; take advantage of the fact that mktime
2201  # will normalize out-of-range values so we don't have to split $minDiff
2202  # into hours and minutes.
2203  $t = mktime( (
2204  (int)substr( $ts, 8, 2 ) ), # Hours
2205  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2206  (int)substr( $ts, 12, 2 ), # Seconds
2207  (int)substr( $ts, 4, 2 ), # Month
2208  (int)substr( $ts, 6, 2 ), # Day
2209  (int)substr( $ts, 0, 4 ) ); # Year
2210 
2211  $date = date( 'YmdHis', $t );
2212  Wikimedia\restoreWarnings();
2213 
2214  return $date;
2215  }
2216 
2232  function dateFormat( $usePrefs = true ) {
2233  global $wgUser;
2234 
2235  if ( is_bool( $usePrefs ) ) {
2236  if ( $usePrefs ) {
2237  $datePreference = $wgUser->getDatePreference();
2238  } else {
2239  $datePreference = (string)User::getDefaultOption( 'date' );
2240  }
2241  } else {
2242  $datePreference = (string)$usePrefs;
2243  }
2244 
2245  // return int
2246  if ( $datePreference == '' ) {
2247  return 'default';
2248  }
2249 
2250  return $datePreference;
2251  }
2252 
2263  function getDateFormatString( $type, $pref ) {
2264  $wasDefault = false;
2265  if ( $pref == 'default' ) {
2266  $wasDefault = true;
2267  $pref = $this->getDefaultDateFormat();
2268  }
2269 
2270  if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
2271  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2272 
2273  if ( $type === 'pretty' && $df === null ) {
2274  $df = $this->getDateFormatString( 'date', $pref );
2275  }
2276 
2277  if ( !$wasDefault && $df === null ) {
2278  $pref = $this->getDefaultDateFormat();
2279  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2280  }
2281 
2282  $this->dateFormatStrings[$type][$pref] = $df;
2283  }
2284  return $this->dateFormatStrings[$type][$pref];
2285  }
2286 
2297  public function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
2298  $ts = wfTimestamp( TS_MW, $ts );
2299  if ( $adj ) {
2300  $ts = $this->userAdjust( $ts, $timecorrection );
2301  }
2302  $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
2303  return $this->sprintfDate( $df, $ts );
2304  }
2305 
2316  public function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
2317  $ts = wfTimestamp( TS_MW, $ts );
2318  if ( $adj ) {
2319  $ts = $this->userAdjust( $ts, $timecorrection );
2320  }
2321  $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
2322  return $this->sprintfDate( $df, $ts );
2323  }
2324 
2336  public function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
2337  $ts = wfTimestamp( TS_MW, $ts );
2338  if ( $adj ) {
2339  $ts = $this->userAdjust( $ts, $timecorrection );
2340  }
2341  $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
2342  return $this->sprintfDate( $df, $ts );
2343  }
2344 
2355  public function formatDuration( $seconds, array $chosenIntervals = [] ) {
2356  $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals );
2357 
2358  $segments = [];
2359 
2360  foreach ( $intervals as $intervalName => $intervalValue ) {
2361  // Messages: duration-seconds, duration-minutes, duration-hours, duration-days, duration-weeks,
2362  // duration-years, duration-decades, duration-centuries, duration-millennia
2363  $message = wfMessage( 'duration-' . $intervalName )->numParams( $intervalValue );
2364  $segments[] = $message->inLanguage( $this )->escaped();
2365  }
2366 
2367  return $this->listToText( $segments );
2368  }
2369 
2381  public function getDurationIntervals( $seconds, array $chosenIntervals = [] ) {
2382  if ( empty( $chosenIntervals ) ) {
2383  $chosenIntervals = [
2384  'millennia',
2385  'centuries',
2386  'decades',
2387  'years',
2388  'days',
2389  'hours',
2390  'minutes',
2391  'seconds'
2392  ];
2393  }
2394 
2395  $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2396  $sortedNames = array_keys( $intervals );
2397  $smallestInterval = array_pop( $sortedNames );
2398 
2399  $segments = [];
2400 
2401  foreach ( $intervals as $name => $length ) {
2402  $value = floor( $seconds / $length );
2403 
2404  if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2405  $seconds -= $value * $length;
2406  $segments[$name] = $value;
2407  }
2408  }
2409 
2410  return $segments;
2411  }
2412 
2432  private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
2433  $ts = wfTimestamp( TS_MW, $ts );
2434  $options += [ 'timecorrection' => true, 'format' => true ];
2435  if ( $options['timecorrection'] !== false ) {
2436  if ( $options['timecorrection'] === true ) {
2437  $offset = $user->getOption( 'timecorrection' );
2438  } else {
2439  $offset = $options['timecorrection'];
2440  }
2441  $ts = $this->userAdjust( $ts, $offset );
2442  }
2443  if ( $options['format'] === true ) {
2444  $format = $user->getDatePreference();
2445  } else {
2446  $format = $options['format'];
2447  }
2448  $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) );
2449  return $this->sprintfDate( $df, $ts );
2450  }
2451 
2471  public function userDate( $ts, User $user, array $options = [] ) {
2472  return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
2473  }
2474 
2494  public function userTime( $ts, User $user, array $options = [] ) {
2495  return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
2496  }
2497 
2517  public function userTimeAndDate( $ts, User $user, array $options = [] ) {
2518  return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
2519  }
2520 
2536  public function getHumanTimestamp(
2537  MWTimestamp $time, MWTimestamp $relativeTo = null, User $user = null
2538  ) {
2539  if ( $relativeTo === null ) {
2540  $relativeTo = new MWTimestamp();
2541  }
2542  if ( $user === null ) {
2543  $user = RequestContext::getMain()->getUser();
2544  }
2545 
2546  // Adjust for the user's timezone.
2547  $offsetThis = $time->offsetForUser( $user );
2548  $offsetRel = $relativeTo->offsetForUser( $user );
2549 
2550  $ts = '';
2551  if ( Hooks::run( 'GetHumanTimestamp', [ &$ts, $time, $relativeTo, $user, $this ] ) ) {
2552  $ts = $this->getHumanTimestampInternal( $time, $relativeTo, $user );
2553  }
2554 
2555  // Reset the timezone on the objects.
2556  $time->timestamp->sub( $offsetThis );
2557  $relativeTo->timestamp->sub( $offsetRel );
2558 
2559  return $ts;
2560  }
2561 
2573  private function getHumanTimestampInternal(
2574  MWTimestamp $ts, MWTimestamp $relativeTo, User $user
2575  ) {
2576  $diff = $ts->diff( $relativeTo );
2577  $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) -
2578  (int)$relativeTo->timestamp->format( 'w' ) );
2579  $days = $diff->days ?: (int)$diffDay;
2580  if ( $diff->invert || $days > 5
2581  && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' )
2582  ) {
2583  // Timestamps are in different years: use full timestamp
2584  // Also do full timestamp for future dates
2588  $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
2589  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2590  } elseif ( $days > 5 ) {
2591  // Timestamps are in same year, but more than 5 days ago: show day and month only.
2592  $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
2593  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2594  } elseif ( $days > 1 ) {
2595  // Timestamp within the past week: show the day of the week and time
2596  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2597  $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
2598  // Messages:
2599  // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
2600  $ts = wfMessage( "$weekday-at" )
2601  ->inLanguage( $this )
2602  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2603  ->text();
2604  } elseif ( $days == 1 ) {
2605  // Timestamp was yesterday: say 'yesterday' and the time.
2606  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2607  $ts = wfMessage( 'yesterday-at' )
2608  ->inLanguage( $this )
2609  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2610  ->text();
2611  } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2612  // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
2613  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2614  $ts = wfMessage( 'today-at' )
2615  ->inLanguage( $this )
2616  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2617  ->text();
2618 
2619  // From here on in, the timestamp was soon enough ago so that we can simply say
2620  // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
2621  } elseif ( $diff->h == 1 ) {
2622  // Less than 90 minutes, but more than an hour ago.
2623  $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2624  } elseif ( $diff->i >= 1 ) {
2625  // A few minutes ago.
2626  $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2627  } elseif ( $diff->s >= 30 ) {
2628  // Less than a minute, but more than 30 sec ago.
2629  $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2630  } else {
2631  // Less than 30 seconds ago.
2632  $ts = wfMessage( 'just-now' )->text();
2633  }
2634 
2635  return $ts;
2636  }
2637 
2642  public function getMessage( $key ) {
2643  return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
2644  }
2645 
2649  function getAllMessages() {
2650  return self::$dataCache->getItem( $this->mCode, 'messages' );
2651  }
2652 
2659  public function iconv( $in, $out, $string ) {
2660  # Even with //IGNORE iconv can whine about illegal characters in
2661  # *input* string. We just ignore those too.
2662  # REF: https://bugs.php.net/bug.php?id=37166
2663  # REF: https://phabricator.wikimedia.org/T18885
2664  Wikimedia\suppressWarnings();
2665  $text = iconv( $in, $out . '//IGNORE', $string );
2666  Wikimedia\restoreWarnings();
2667  return $text;
2668  }
2669 
2670  // callback functions for ucwords(), ucwordbreaks()
2671 
2677  return $this->ucfirst( $matches[1] );
2678  }
2679 
2685  return mb_strtoupper( $matches[0] );
2686  }
2687 
2693  return mb_strtoupper( $matches[0] );
2694  }
2695 
2703  public function ucfirst( $str ) {
2704  $o = ord( $str );
2705  if ( $o < 96 ) { // if already uppercase...
2706  return $str;
2707  } elseif ( $o < 128 ) {
2708  return ucfirst( $str ); // use PHP's ucfirst()
2709  } else {
2710  // fall back to more complex logic in case of multibyte strings
2711  return $this->uc( $str, true );
2712  }
2713  }
2714 
2723  public function uc( $str, $first = false ) {
2724  if ( $first ) {
2725  if ( $this->isMultibyte( $str ) ) {
2726  return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2727  } else {
2728  return ucfirst( $str );
2729  }
2730  } else {
2731  return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2732  }
2733  }
2734 
2739  function lcfirst( $str ) {
2740  $o = ord( $str );
2741  if ( !$o ) {
2742  return strval( $str );
2743  } elseif ( $o >= 128 ) {
2744  return $this->lc( $str, true );
2745  } elseif ( $o > 96 ) {
2746  return $str;
2747  } else {
2748  $str[0] = strtolower( $str[0] );
2749  return $str;
2750  }
2751  }
2752 
2758  function lc( $str, $first = false ) {
2759  if ( $first ) {
2760  if ( $this->isMultibyte( $str ) ) {
2761  return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2762  } else {
2763  return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2764  }
2765  } else {
2766  return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2767  }
2768  }
2769 
2774  function isMultibyte( $str ) {
2775  return strlen( $str ) !== mb_strlen( $str );
2776  }
2777 
2782  function ucwords( $str ) {
2783  if ( $this->isMultibyte( $str ) ) {
2784  $str = $this->lc( $str );
2785 
2786  // regexp to find first letter in each word (i.e. after each space)
2787  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2788 
2789  // function to use to capitalize a single char
2790  return preg_replace_callback(
2791  $replaceRegexp,
2792  [ $this, 'ucwordsCallbackMB' ],
2793  $str
2794  );
2795  } else {
2796  return ucwords( strtolower( $str ) );
2797  }
2798  }
2799 
2806  function ucwordbreaks( $str ) {
2807  if ( $this->isMultibyte( $str ) ) {
2808  $str = $this->lc( $str );
2809 
2810  // since \b doesn't work for UTF-8, we explicitely define word break chars
2811  $breaks = "[ \-\(\)\}\{\.,\?!]";
2812 
2813  // find first letter after word break
2814  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2815  "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2816 
2817  return preg_replace_callback(
2818  $replaceRegexp,
2819  [ $this, 'ucwordbreaksCallbackMB' ],
2820  $str
2821  );
2822  } else {
2823  return preg_replace_callback(
2824  '/\b([\w\x80-\xff]+)\b/',
2825  [ $this, 'ucwordbreaksCallbackAscii' ],
2826  $str
2827  );
2828  }
2829  }
2830 
2846  function caseFold( $s ) {
2847  return $this->uc( $s );
2848  }
2849 
2855  function checkTitleEncoding( $s ) {
2856  if ( is_array( $s ) ) {
2857  throw new MWException( 'Given array to checkTitleEncoding.' );
2858  }
2859  if ( StringUtils::isUtf8( $s ) ) {
2860  return $s;
2861  }
2862 
2863  return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
2864  }
2865 
2870  return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
2871  }
2872 
2881  function hasWordBreaks() {
2882  return true;
2883  }
2884 
2892  function segmentByWord( $string ) {
2893  return $string;
2894  }
2895 
2903  function normalizeForSearch( $string ) {
2904  return self::convertDoubleWidth( $string );
2905  }
2906 
2915  protected static function convertDoubleWidth( $string ) {
2916  static $full = null;
2917  static $half = null;
2918 
2919  if ( $full === null ) {
2920  $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2921  $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2922  $full = str_split( $fullWidth, 3 );
2923  $half = str_split( $halfWidth );
2924  }
2925 
2926  $string = str_replace( $full, $half, $string );
2927  return $string;
2928  }
2929 
2935  protected static function insertSpace( $string, $pattern ) {
2936  $string = preg_replace( $pattern, " $1 ", $string );
2937  $string = preg_replace( '/ +/', ' ', $string );
2938  return $string;
2939  }
2940 
2945  function convertForSearchResult( $termsArray ) {
2946  # some languages, e.g. Chinese, need to do a conversion
2947  # in order for search results to be displayed correctly
2948  return $termsArray;
2949  }
2950 
2957  function firstChar( $s ) {
2958  $matches = [];
2959  preg_match(
2960  '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2961  '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2962  $s,
2963  $matches
2964  );
2965 
2966  if ( isset( $matches[1] ) ) {
2967  if ( strlen( $matches[1] ) != 3 ) {
2968  return $matches[1];
2969  }
2970 
2971  // Break down Hangul syllables to grab the first jamo
2972  $code = UtfNormal\Utils::utf8ToCodepoint( $matches[1] );
2973  if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2974  return $matches[1];
2975  } elseif ( $code < 0xb098 ) {
2976  return "\u{3131}";
2977  } elseif ( $code < 0xb2e4 ) {
2978  return "\u{3134}";
2979  } elseif ( $code < 0xb77c ) {
2980  return "\u{3137}";
2981  } elseif ( $code < 0xb9c8 ) {
2982  return "\u{3139}";
2983  } elseif ( $code < 0xbc14 ) {
2984  return "\u{3141}";
2985  } elseif ( $code < 0xc0ac ) {
2986  return "\u{3142}";
2987  } elseif ( $code < 0xc544 ) {
2988  return "\u{3145}";
2989  } elseif ( $code < 0xc790 ) {
2990  return "\u{3147}";
2991  } elseif ( $code < 0xcc28 ) {
2992  return "\u{3148}";
2993  } elseif ( $code < 0xce74 ) {
2994  return "\u{314A}";
2995  } elseif ( $code < 0xd0c0 ) {
2996  return "\u{314B}";
2997  } elseif ( $code < 0xd30c ) {
2998  return "\u{314C}";
2999  } elseif ( $code < 0xd558 ) {
3000  return "\u{314D}";
3001  } else {
3002  return "\u{314E}";
3003  }
3004  } else {
3005  return '';
3006  }
3007  }
3008 
3012  function initEncoding() {
3013  wfDeprecated( __METHOD__, '1.28' );
3014  // No-op.
3015  }
3016 
3022  function recodeForEdit( $s ) {
3023  wfDeprecated( __METHOD__, '1.28' );
3024  return $s;
3025  }
3026 
3032  function recodeInput( $s ) {
3033  wfDeprecated( __METHOD__, '1.28' );
3034  return $s;
3035  }
3036 
3048  public function normalize( $s ) {
3049  global $wgAllUnicodeFixes;
3050  $s = UtfNormal\Validator::cleanUp( $s );
3051  if ( $wgAllUnicodeFixes ) {
3052  $s = $this->transformUsingPairFile( 'normalize-ar.php', $s );
3053  $s = $this->transformUsingPairFile( 'normalize-ml.php', $s );
3054  }
3055 
3056  return $s;
3057  }
3058 
3073  protected function transformUsingPairFile( $file, $string ) {
3074  if ( !isset( $this->transformData[$file] ) ) {
3075  global $IP;
3076  $data = require "$IP/languages/data/{$file}";
3077  $this->transformData[$file] = new ReplacementArray( $data );
3078  }
3079  return $this->transformData[$file]->replace( $string );
3080  }
3081 
3087  function isRTL() {
3088  return self::$dataCache->getItem( $this->mCode, 'rtl' );
3089  }
3090 
3095  function getDir() {
3096  return $this->isRTL() ? 'rtl' : 'ltr';
3097  }
3098 
3107  function alignStart() {
3108  return $this->isRTL() ? 'right' : 'left';
3109  }
3110 
3119  function alignEnd() {
3120  return $this->isRTL() ? 'left' : 'right';
3121  }
3122 
3134  function getDirMarkEntity( $opposite = false ) {
3135  if ( $opposite ) {
3136  return $this->isRTL() ? '&lrm;' : '&rlm;';
3137  }
3138  return $this->isRTL() ? '&rlm;' : '&lrm;';
3139  }
3140 
3151  function getDirMark( $opposite = false ) {
3152  $lrm = "\u{200E}"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3153  $rlm = "\u{200F}"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3154  if ( $opposite ) {
3155  return $this->isRTL() ? $lrm : $rlm;
3156  }
3157  return $this->isRTL() ? $rlm : $lrm;
3158  }
3159 
3163  function capitalizeAllNouns() {
3164  return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
3165  }
3166 
3174  function getArrow( $direction = 'forwards' ) {
3175  switch ( $direction ) {
3176  case 'forwards':
3177  return $this->isRTL() ? '←' : '→';
3178  case 'backwards':
3179  return $this->isRTL() ? '→' : '←';
3180  case 'left':
3181  return '←';
3182  case 'right':
3183  return '→';
3184  case 'up':
3185  return '↑';
3186  case 'down':
3187  return '↓';
3188  }
3189  }
3190 
3196  function linkPrefixExtension() {
3197  return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
3198  }
3199 
3204  function getMagicWords() {
3205  return self::$dataCache->getItem( $this->mCode, 'magicWords' );
3206  }
3207 
3211  protected function doMagicHook() {
3212  if ( $this->mMagicHookDone ) {
3213  return;
3214  }
3215  $this->mMagicHookDone = true;
3216  Hooks::run( 'LanguageGetMagic', [ &$this->mMagicExtensions, $this->getCode() ], '1.16' );
3217  }
3218 
3224  function getMagic( $mw ) {
3225  // Saves a function call
3226  if ( !$this->mMagicHookDone ) {
3227  $this->doMagicHook();
3228  }
3229 
3230  if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3231  $rawEntry = $this->mMagicExtensions[$mw->mId];
3232  } else {
3233  $rawEntry = self::$dataCache->getSubitem(
3234  $this->mCode, 'magicWords', $mw->mId );
3235  }
3236 
3237  if ( !is_array( $rawEntry ) ) {
3238  wfWarn( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3239  } else {
3240  $mw->mCaseSensitive = $rawEntry[0];
3241  $mw->mSynonyms = array_slice( $rawEntry, 1 );
3242  }
3243  }
3244 
3250  function addMagicWordsByLang( $newWords ) {
3251  $fallbackChain = $this->getFallbackLanguages();
3252  $fallbackChain = array_reverse( $fallbackChain );
3253  foreach ( $fallbackChain as $code ) {
3254  if ( isset( $newWords[$code] ) ) {
3255  $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
3256  }
3257  }
3258  }
3259 
3266  // Cache aliases because it may be slow to load them
3267  if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3268  // Initialise array
3269  $this->mExtendedSpecialPageAliases =
3270  self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
3271  Hooks::run( 'LanguageGetSpecialPageAliases',
3272  [ &$this->mExtendedSpecialPageAliases, $this->getCode() ], '1.16' );
3273  }
3274 
3276  }
3277 
3284  function emphasize( $text ) {
3285  return "<em>$text</em>";
3286  }
3287 
3310  public function formatNum( $number, $nocommafy = false ) {
3311  global $wgTranslateNumerals;
3312  if ( !$nocommafy ) {
3313  $number = $this->commafy( $number );
3314  $s = $this->separatorTransformTable();
3315  if ( $s ) {
3316  $number = strtr( $number, $s );
3317  }
3318  }
3319 
3320  if ( $wgTranslateNumerals ) {
3321  $s = $this->digitTransformTable();
3322  if ( $s ) {
3323  $number = strtr( $number, $s );
3324  }
3325  }
3326 
3327  return (string)$number;
3328  }
3329 
3338  public function formatNumNoSeparators( $number ) {
3339  return $this->formatNum( $number, true );
3340  }
3341 
3346  public function parseFormattedNumber( $number ) {
3347  $s = $this->digitTransformTable();
3348  if ( $s ) {
3349  // eliminate empty array values such as ''. (T66347)
3350  $s = array_filter( $s );
3351  $number = strtr( $number, array_flip( $s ) );
3352  }
3353 
3354  $s = $this->separatorTransformTable();
3355  if ( $s ) {
3356  // eliminate empty array values such as ''. (T66347)
3357  $s = array_filter( $s );
3358  $number = strtr( $number, array_flip( $s ) );
3359  }
3360 
3361  $number = strtr( $number, [ ',' => '' ] );
3362  return $number;
3363  }
3364 
3371  function commafy( $number ) {
3374  if ( $number === null ) {
3375  return '';
3376  }
3377 
3378  if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) {
3379  // Default grouping is at thousands, use the same for ###,###,### pattern too.
3380  // In some languages it's conventional not to insert a thousands separator
3381  // in numbers that are four digits long (1000-9999).
3382  if ( $minimumGroupingDigits ) {
3383  // Number of '#' characters after last comma in the grouping pattern.
3384  // The pattern is hardcoded here, but this would vary for different patterns.
3385  $primaryGroupingSize = 3;
3386  // Maximum length of a number to suppress digit grouping for.
3387  $maximumLength = $minimumGroupingDigits + $primaryGroupingSize - 1;
3388  if ( preg_match( '/^\-?\d{1,' . $maximumLength . '}(\.\d+)?$/', $number ) ) {
3389  return $number;
3390  }
3391  }
3392  return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $number ) ) );
3393  } else {
3394  // Ref: http://cldr.unicode.org/translation/number-patterns
3395  $sign = "";
3396  if ( intval( $number ) < 0 ) {
3397  // For negative numbers apply the algorithm like positive number and add sign.
3398  $sign = "-";
3399  $number = substr( $number, 1 );
3400  }
3401  $integerPart = [];
3402  $decimalPart = [];
3403  $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches );
3404  preg_match( "/\d+/", $number, $integerPart );
3405  preg_match( "/\.\d*/", $number, $decimalPart );
3406  $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] : "";
3407  if ( $groupedNumber === $number ) {
3408  // the string does not have any number part. Eg: .12345
3409  return $sign . $groupedNumber;
3410  }
3411  $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3412  while ( $start > 0 ) {
3413  $match = $matches[0][$numMatches - 1];
3414  $matchLen = strlen( $match );
3415  $start = $end - $matchLen;
3416  if ( $start < 0 ) {
3417  $start = 0;
3418  }
3419  $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3420  $end = $start;
3421  if ( $numMatches > 1 ) {
3422  // use the last pattern for the rest of the number
3423  $numMatches--;
3424  }
3425  if ( $start > 0 ) {
3426  $groupedNumber = "," . $groupedNumber;
3427  }
3428  }
3429  return $sign . $groupedNumber;
3430  }
3431  }
3432 
3437  return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
3438  }
3439 
3443  function digitTransformTable() {
3444  return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
3445  }
3446 
3451  return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
3452  }
3453 
3458  return self::$dataCache->getItem( $this->mCode, 'minimumGroupingDigits' );
3459  }
3460 
3469  public function listToText( array $list ) {
3470  $itemCount = count( $list );
3471  if ( $itemCount < 1 ) {
3472  return '';
3473  }
3474  $text = array_pop( $list );
3475  if ( $itemCount > 1 ) {
3476  $and = $this->msg( 'and' )->escaped();
3477  $space = $this->msg( 'word-separator' )->escaped();
3478  $comma = '';
3479  if ( $itemCount > 2 ) {
3480  $comma = $this->msg( 'comma-separator' )->escaped();
3481  }
3482  $text = implode( $comma, $list ) . $and . $space . $text;
3483  }
3484  return $text;
3485  }
3486 
3493  function commaList( array $list ) {
3494  return implode(
3495  wfMessage( 'comma-separator' )->inLanguage( $this )->escaped(),
3496  $list
3497  );
3498  }
3499 
3506  function semicolonList( array $list ) {
3507  return implode(
3508  wfMessage( 'semicolon-separator' )->inLanguage( $this )->escaped(),
3509  $list
3510  );
3511  }
3512 
3518  function pipeList( array $list ) {
3519  return implode(
3520  wfMessage( 'pipe-separator' )->inLanguage( $this )->escaped(),
3521  $list
3522  );
3523  }
3524 
3542  function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3543  wfDeprecated( __METHOD__, '1.31' );
3544  return $this->truncateForDatabase( $string, $length, $ellipsis, $adjustLength );
3545  }
3546 
3562  function truncateForDatabase( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3563  return $this->truncateInternal(
3564  $string, $length, $ellipsis, $adjustLength, 'strlen', 'substr'
3565  );
3566  }
3567 
3586  function truncateForVisual( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3587  // Passing encoding to mb_strlen and mb_substr is optional.
3588  // Encoding defaults to mb_internal_encoding(), which is set to UTF-8 in Setup.php, so
3589  // explicit specification of encoding is skipped.
3590  // Note: Both multibyte methods are callables invoked in truncateInternal.
3591  return $this->truncateInternal(
3592  $string, $length, $ellipsis, $adjustLength, 'mb_strlen', 'mb_substr'
3593  );
3594  }
3595 
3612  private function truncateInternal(
3613  $string, $length, $ellipsis, $adjustLength, $measureLength, $getSubstring
3614  ) {
3615  if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
3616  throw new InvalidArgumentException( 'Invalid callback provided' );
3617  }
3618 
3619  # Check if there is no need to truncate
3620  if ( $measureLength( $string ) <= abs( $length ) ) {
3621  return $string; // no need to truncate
3622  }
3623 
3624  # Use the localized ellipsis character
3625  if ( $ellipsis == '...' ) {
3626  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3627  }
3628  if ( $length == 0 ) {
3629  return $ellipsis; // convention
3630  }
3631 
3632  $stringOriginal = $string;
3633  # If ellipsis length is >= $length then we can't apply $adjustLength
3634  if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3635  $string = $ellipsis; // this can be slightly unexpected
3636  # Otherwise, truncate and add ellipsis...
3637  } else {
3638  $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3639  if ( $length > 0 ) {
3640  $length -= $ellipsisLength;
3641  $string = $getSubstring( $string, 0, $length ); // xyz...
3642  $string = $this->removeBadCharLast( $string );
3643  $string = rtrim( $string );
3644  $string = $string . $ellipsis;
3645  } else {
3646  $length += $ellipsisLength;
3647  $string = $getSubstring( $string, $length ); // ...xyz
3648  $string = $this->removeBadCharFirst( $string );
3649  $string = ltrim( $string );
3650  $string = $ellipsis . $string;
3651  }
3652  }
3653 
3654  # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3655  # This check is *not* redundant if $adjustLength, due to the single case where
3656  # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3657  if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3658  return $string;
3659  } else {
3660  return $stringOriginal;
3661  }
3662  }
3663 
3671  protected function removeBadCharLast( $string ) {
3672  if ( $string != '' ) {
3673  $char = ord( $string[strlen( $string ) - 1] );
3674  $m = [];
3675  if ( $char >= 0xc0 ) {
3676  # We got the first byte only of a multibyte char; remove it.
3677  $string = substr( $string, 0, -1 );
3678  } elseif ( $char >= 0x80 &&
3679  // Use the /s modifier (PCRE_DOTALL) so (.*) also matches newlines
3680  preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3681  '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3682  ) {
3683  # We chopped in the middle of a character; remove it
3684  $string = $m[1];
3685  }
3686  }
3687  return $string;
3688  }
3689 
3697  protected function removeBadCharFirst( $string ) {
3698  if ( $string != '' ) {
3699  $char = ord( $string[0] );
3700  if ( $char >= 0x80 && $char < 0xc0 ) {
3701  # We chopped in the middle of a character; remove the whole thing
3702  $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
3703  }
3704  }
3705  return $string;
3706  }
3707 
3723  function truncateHtml( $text, $length, $ellipsis = '...' ) {
3724  # Use the localized ellipsis character
3725  if ( $ellipsis == '...' ) {
3726  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3727  }
3728  # Check if there is clearly no need to truncate
3729  if ( $length <= 0 ) {
3730  return $ellipsis; // no text shown, nothing to format (convention)
3731  } elseif ( strlen( $text ) <= $length ) {
3732  return $text; // string short enough even *with* HTML (short-circuit)
3733  }
3734 
3735  $dispLen = 0; // innerHTML legth so far
3736  $testingEllipsis = false; // checking if ellipses will make string longer/equal?
3737  $tagType = 0; // 0-open, 1-close
3738  $bracketState = 0; // 1-tag start, 2-tag name, 0-neither
3739  $entityState = 0; // 0-not entity, 1-entity
3740  $tag = $ret = ''; // accumulated tag name, accumulated result string
3741  $openTags = []; // open tag stack
3742  $maybeState = null; // possible truncation state
3743 
3744  $textLen = strlen( $text );
3745  $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated
3746  for ( $pos = 0; true; ++$pos ) {
3747  # Consider truncation once the display length has reached the maximim.
3748  # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3749  # Check that we're not in the middle of a bracket/entity...
3750  if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3751  if ( !$testingEllipsis ) {
3752  $testingEllipsis = true;
3753  # Save where we are; we will truncate here unless there turn out to
3754  # be so few remaining characters that truncation is not necessary.
3755  if ( !$maybeState ) { // already saved? ($neLength = 0 case)
3756  $maybeState = [ $ret, $openTags ]; // save state
3757  }
3758  } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3759  # String in fact does need truncation, the truncation point was OK.
3760  list( $ret, $openTags ) = $maybeState; // reload state
3761  $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix
3762  $ret .= $ellipsis; // add ellipsis
3763  break;
3764  }
3765  }
3766  if ( $pos >= $textLen ) {
3767  break; // extra iteration just for above checks
3768  }
3769 
3770  # Read the next char...
3771  $ch = $text[$pos];
3772  $lastCh = $pos ? $text[$pos - 1] : '';
3773  $ret .= $ch; // add to result string
3774  if ( $ch == '<' ) {
3775  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML
3776  $entityState = 0; // for bad HTML
3777  $bracketState = 1; // tag started (checking for backslash)
3778  } elseif ( $ch == '>' ) {
3779  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags );
3780  $entityState = 0; // for bad HTML
3781  $bracketState = 0; // out of brackets
3782  } elseif ( $bracketState == 1 ) {
3783  if ( $ch == '/' ) {
3784  $tagType = 1; // close tag (e.g. "</span>")
3785  } else {
3786  $tagType = 0; // open tag (e.g. "<span>")
3787  $tag .= $ch;
3788  }
3789  $bracketState = 2; // building tag name
3790  } elseif ( $bracketState == 2 ) {
3791  if ( $ch != ' ' ) {
3792  $tag .= $ch;
3793  } else {
3794  // Name found (e.g. "<a href=..."), add on tag attributes...
3795  $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 );
3796  }
3797  } elseif ( $bracketState == 0 ) {
3798  if ( $entityState ) {
3799  if ( $ch == ';' ) {
3800  $entityState = 0;
3801  $dispLen++; // entity is one displayed char
3802  }
3803  } else {
3804  if ( $neLength == 0 && !$maybeState ) {
3805  // Save state without $ch. We want to *hit* the first
3806  // display char (to get tags) but not *use* it if truncating.
3807  $maybeState = [ substr( $ret, 0, -1 ), $openTags ];
3808  }
3809  if ( $ch == '&' ) {
3810  $entityState = 1; // entity found, (e.g. "&#160;")
3811  } else {
3812  $dispLen++; // this char is displayed
3813  // Add the next $max display text chars after this in one swoop...
3814  $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3815  $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max );
3816  $dispLen += $skipped;
3817  $pos += $skipped;
3818  }
3819  }
3820  }
3821  }
3822  // Close the last tag if left unclosed by bad HTML
3823  $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags );
3824  while ( count( $openTags ) > 0 ) {
3825  $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags
3826  }
3827  return $ret;
3828  }
3829 
3841  private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
3842  if ( $len === null ) {
3843  $len = -1; // -1 means "no limit" for strcspn
3844  } elseif ( $len < 0 ) {
3845  $len = 0; // sanity
3846  }
3847  $skipCount = 0;
3848  if ( $start < strlen( $text ) ) {
3849  $skipCount = strcspn( $text, $search, $start, $len );
3850  $ret .= substr( $text, $start, $skipCount );
3851  }
3852  return $skipCount;
3853  }
3854 
3864  private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
3865  $tag = ltrim( $tag );
3866  if ( $tag != '' ) {
3867  if ( $tagType == 0 && $lastCh != '/' ) {
3868  $openTags[] = $tag; // tag opened (didn't close itself)
3869  } elseif ( $tagType == 1 ) {
3870  if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3871  array_pop( $openTags ); // tag closed
3872  }
3873  }
3874  $tag = '';
3875  }
3876  }
3877 
3886  function convertGrammar( $word, $case ) {
3887  global $wgGrammarForms;
3888  if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
3889  return $wgGrammarForms[$this->getCode()][$case][$word];
3890  }
3891 
3893 
3894  if ( isset( $grammarTransformations[$case] ) ) {
3895  $forms = $grammarTransformations[$case];
3896 
3897  // Some names of grammar rules are aliases for other rules.
3898  // In such cases the value is a string rather than object,
3899  // so load the actual rules.
3900  if ( is_string( $forms ) ) {
3901  $forms = $grammarTransformations[$forms];
3902  }
3903 
3904  foreach ( array_values( $forms ) as $rule ) {
3905  $form = $rule[0];
3906 
3907  if ( $form === '@metadata' ) {
3908  continue;
3909  }
3910 
3911  $replacement = $rule[1];
3912 
3913  $regex = '/' . addcslashes( $form, '/' ) . '/u';
3914  $patternMatches = preg_match( $regex, $word );
3915 
3916  if ( $patternMatches === false ) {
3917  wfLogWarning(
3918  'An error occurred while processing grammar. ' .
3919  "Word: '$word'. Regex: /$form/."
3920  );
3921  } elseif ( $patternMatches === 1 ) {
3922  $word = preg_replace( $regex, $replacement, $word );
3923 
3924  break;
3925  }
3926  }
3927  }
3928 
3929  return $word;
3930  }
3931 
3937  function getGrammarForms() {
3938  global $wgGrammarForms;
3939  if ( isset( $wgGrammarForms[$this->getCode()] )
3940  && is_array( $wgGrammarForms[$this->getCode()] )
3941  ) {
3942  return $wgGrammarForms[$this->getCode()];
3943  }
3944 
3945  return [];
3946  }
3947 
3957  public function getGrammarTransformations() {
3958  $languageCode = $this->getCode();
3959 
3960  if ( self::$grammarTransformations === null ) {
3961  self::$grammarTransformations = new MapCacheLRU( 10 );
3962  }
3963 
3964  if ( self::$grammarTransformations->has( $languageCode ) ) {
3965  return self::$grammarTransformations->get( $languageCode );
3966  }
3967 
3968  $data = [];
3969 
3970  $grammarDataFile = __DIR__ . "/data/grammarTransformations/$languageCode.json";
3971  if ( is_readable( $grammarDataFile ) ) {
3972  $data = FormatJson::decode(
3973  file_get_contents( $grammarDataFile ),
3974  true
3975  );
3976 
3977  if ( $data === null ) {
3978  throw new MWException( "Invalid grammar data for \"$languageCode\"." );
3979  }
3980 
3981  self::$grammarTransformations->set( $languageCode, $data );
3982  }
3983 
3984  return $data;
3985  }
3986 
4006  function gender( $gender, $forms ) {
4007  if ( !count( $forms ) ) {
4008  return '';
4009  }
4010  $forms = $this->preConvertPlural( $forms, 2 );
4011  if ( $gender === 'male' ) {
4012  return $forms[0];
4013  }
4014  if ( $gender === 'female' ) {
4015  return $forms[1];
4016  }
4017  return $forms[2] ?? $forms[0];
4018  }
4019 
4035  function convertPlural( $count, $forms ) {
4036  // Handle explicit n=pluralform cases
4037  $forms = $this->handleExplicitPluralForms( $count, $forms );
4038  if ( is_string( $forms ) ) {
4039  return $forms;
4040  }
4041  if ( !count( $forms ) ) {
4042  return '';
4043  }
4044 
4045  $pluralForm = $this->getPluralRuleIndexNumber( $count );
4046  $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4047  return $forms[$pluralForm];
4048  }
4049 
4065  protected function handleExplicitPluralForms( $count, array $forms ) {
4066  foreach ( $forms as $index => $form ) {
4067  if ( preg_match( '/\d+=/i', $form ) ) {
4068  $pos = strpos( $form, '=' );
4069  if ( substr( $form, 0, $pos ) === (string)$count ) {
4070  return substr( $form, $pos + 1 );
4071  }
4072  unset( $forms[$index] );
4073  }
4074  }
4075  return array_values( $forms );
4076  }
4077 
4086  protected function preConvertPlural( /* Array */ $forms, $count ) {
4087  while ( count( $forms ) < $count ) {
4088  $forms[] = $forms[count( $forms ) - 1];
4089  }
4090  return $forms;
4091  }
4092 
4109  public function embedBidi( $text = '' ) {
4110  $dir = self::strongDirFromContent( $text );
4111  if ( $dir === 'ltr' ) {
4112  // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING
4113  return self::$lre . $text . self::$pdf;
4114  }
4115  if ( $dir === 'rtl' ) {
4116  // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING
4117  return self::$rle . $text . self::$pdf;
4118  }
4119  // No strong directionality: do not wrap
4120  return $text;
4121  }
4122 
4136  function translateBlockExpiry( $str, User $user = null, $now = 0 ) {
4137  $duration = SpecialBlock::getSuggestedDurations( $this );
4138  foreach ( $duration as $show => $value ) {
4139  if ( strcmp( $str, $value ) == 0 ) {
4140  return htmlspecialchars( trim( $show ) );
4141  }
4142  }
4143 
4144  if ( wfIsInfinity( $str ) ) {
4145  foreach ( $duration as $show => $value ) {
4146  if ( wfIsInfinity( $value ) ) {
4147  return htmlspecialchars( trim( $show ) );
4148  }
4149  }
4150  }
4151 
4152  // If all else fails, return a standard duration or timestamp description.
4153  $time = strtotime( $str, $now );
4154  if ( $time === false ) { // Unknown format. Return it as-is in case.
4155  return $str;
4156  } elseif ( $time !== strtotime( $str, $now + 1 ) ) { // It's a relative timestamp.
4157  // The result differs based on current time, so the difference
4158  // is a fixed duration length.
4159  return $this->formatDuration( $time - $now );
4160  } else { // It's an absolute timestamp.
4161  if ( $time === 0 ) {
4162  // wfTimestamp() handles 0 as current time instead of epoch.
4163  $time = '19700101000000';
4164  }
4165  if ( $user ) {
4166  return $this->userTimeAndDate( $time, $user );
4167  }
4168  return $this->timeanddate( $time );
4169  }
4170  }
4171 
4179  public function segmentForDiff( $text ) {
4180  return $text;
4181  }
4182 
4189  public function unsegmentForDiff( $text ) {
4190  return $text;
4191  }
4192 
4199  public function getConverter() {
4200  return $this->mConverter;
4201  }
4202 
4211  public function autoConvert( $text, $variant = false ) {
4212  return $this->mConverter->autoConvert( $text, $variant );
4213  }
4214 
4221  public function autoConvertToAllVariants( $text ) {
4222  return $this->mConverter->autoConvertToAllVariants( $text );
4223  }
4224 
4236  public function convert( $text ) {
4237  return $this->mConverter->convert( $text );
4238  }
4239 
4246  public function convertTitle( $title ) {
4247  return $this->mConverter->convertTitle( $title );
4248  }
4249 
4258  public function convertNamespace( $ns, $variant = null ) {
4259  return $this->mConverter->convertNamespace( $ns, $variant );
4260  }
4261 
4267  public function hasVariants() {
4268  return count( $this->getVariants() ) > 1;
4269  }
4270 
4281  public function hasVariant( $variant ) {
4282  return $variant && ( $variant === $this->mConverter->validateVariant( $variant ) );
4283  }
4284 
4292  public function convertHtml( $text, $isTitle = false ) {
4293  return htmlspecialchars( $this->convert( $text, $isTitle ) );
4294  }
4295 
4300  public function convertCategoryKey( $key ) {
4301  return $this->mConverter->convertCategoryKey( $key );
4302  }
4303 
4310  public function getVariants() {
4311  return $this->mConverter->getVariants();
4312  }
4313 
4317  public function getPreferredVariant() {
4318  return $this->mConverter->getPreferredVariant();
4319  }
4320 
4324  public function getDefaultVariant() {
4325  return $this->mConverter->getDefaultVariant();
4326  }
4327 
4331  public function getURLVariant() {
4332  return $this->mConverter->getURLVariant();
4333  }
4334 
4347  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
4348  $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
4349  }
4350 
4357  function getExtraHashOptions() {
4358  return $this->mConverter->getExtraHashOptions();
4359  }
4360 
4368  public function getParsedTitle() {
4369  return $this->mConverter->getParsedTitle();
4370  }
4371 
4378  public function updateConversionTable( Title $title ) {
4379  $this->mConverter->updateConversionTable( $title );
4380  }
4381 
4398  public function markNoConversion( $text, $noParse = false ) {
4399  wfDeprecated( __METHOD__, '1.32' );
4400  // Excluding protocal-relative URLs may avoid many false positives.
4401  if ( $noParse || preg_match( '/^(?:' . wfUrlProtocolsWithoutProtRel() . ')/', $text ) ) {
4402  return $this->mConverter->markNoConversion( $text );
4403  } else {
4404  return $text;
4405  }
4406  }
4407 
4414  public function linkTrail() {
4415  return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
4416  }
4417 
4424  public function linkPrefixCharset() {
4425  return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
4426  }
4427 
4435  public function getParentLanguage() {
4436  if ( $this->mParentLanguage !== false ) {
4437  return $this->mParentLanguage;
4438  }
4439 
4440  $code = explode( '-', $this->getCode() )[0];
4441  if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
4442  $this->mParentLanguage = null;
4443  return null;
4444  }
4445  $lang = self::factory( $code );
4446  if ( !$lang->hasVariant( $this->getCode() ) ) {
4447  $this->mParentLanguage = null;
4448  return null;
4449  }
4450 
4451  $this->mParentLanguage = $lang;
4452  return $lang;
4453  }
4454 
4462  public function equals( Language $lang ) {
4463  return $lang === $this || $lang->getCode() === $this->mCode;
4464  }
4465 
4474  public function getCode() {
4475  return $this->mCode;
4476  }
4477 
4488  public function getHtmlCode() {
4489  if ( is_null( $this->mHtmlCode ) ) {
4490  $this->mHtmlCode = LanguageCode::bcp47( $this->getCode() );
4491  }
4492  return $this->mHtmlCode;
4493  }
4494 
4499  public function setCode( $code ) {
4500  $this->mCode = $code;
4501  // Ensure we don't leave incorrect cached data lying around
4502  $this->mHtmlCode = null;
4503  $this->mParentLanguage = false;
4504  }
4505 
4513  public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
4514  $m = null;
4515  preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' .
4516  preg_quote( $suffix, '/' ) . '/', $filename, $m );
4517  if ( !count( $m ) ) {
4518  return false;
4519  }
4520  return str_replace( '_', '-', strtolower( $m[1] ) );
4521  }
4522 
4528  public static function classFromCode( $code, $fallback = true ) {
4529  if ( $fallback && $code == 'en' ) {
4530  return 'Language';
4531  } else {
4532  return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
4533  }
4534  }
4535 
4544  public static function getFileName( $prefix, $code, $suffix = '.php' ) {
4545  if ( !self::isValidBuiltInCode( $code ) ) {
4546  throw new MWException( "Invalid language code \"$code\"" );
4547  }
4548 
4549  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
4550  }
4551 
4556  public static function getMessagesFileName( $code ) {
4557  global $IP;
4558  $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
4559  Hooks::run( 'Language::getMessagesFileName', [ $code, &$file ] );
4560  return $file;
4561  }
4562 
4569  public static function getJsonMessagesFileName( $code ) {
4570  global $IP;
4571 
4572  if ( !self::isValidBuiltInCode( $code ) ) {
4573  throw new MWException( "Invalid language code \"$code\"" );
4574  }
4575 
4576  return "$IP/languages/i18n/$code.json";
4577  }
4578 
4586  public static function getFallbackFor( $code ) {
4587  $fallbacks = self::getFallbacksFor( $code );
4588  if ( $fallbacks ) {
4589  return $fallbacks[0];
4590  }
4591  return false;
4592  }
4593 
4604  public static function getFallbacksFor( $code, $mode = self::MESSAGES_FALLBACKS ) {
4605  if ( $code === 'en' || !self::isValidBuiltInCode( $code ) ) {
4606  return [];
4607  }
4608  switch ( $mode ) {
4610  // For unknown languages, fallbackSequence returns an empty array,
4611  // hardcode fallback to 'en' in that case as English messages are
4612  // always defined.
4613  return self::getLocalisationCache()->getItem( $code, 'fallbackSequence' ) ?: [ 'en' ];
4615  // Use this mode when you don't want to fallback to English unless
4616  // explicitly defined, for example when you have language-variant icons
4617  // and an international language-independent fallback.
4618  return self::getLocalisationCache()->getItem( $code, 'originalFallbackSequence' );
4619  default:
4620  throw new MWException( "Invalid fallback mode \"$mode\"" );
4621  }
4622  }
4623 
4632  public static function getFallbacksIncludingSiteLanguage( $code ) {
4633  global $wgLanguageCode;
4634 
4635  // Usually, we will only store a tiny number of fallback chains, so we
4636  // keep them in static memory.
4637  $cacheKey = "{$code}-{$wgLanguageCode}";
4638 
4639  if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4640  $fallbacks = self::getFallbacksFor( $code );
4641 
4642  // Append the site's fallback chain, including the site language itself
4643  $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
4644  array_unshift( $siteFallbacks, $wgLanguageCode );
4645 
4646  // Eliminate any languages already included in the chain
4647  $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4648 
4649  self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4650  }
4651  return self::$fallbackLanguageCache[$cacheKey];
4652  }
4653 
4663  public static function getMessagesFor( $code ) {
4664  return self::getLocalisationCache()->getItem( $code, 'messages' );
4665  }
4666 
4675  public static function getMessageFor( $key, $code ) {
4676  return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
4677  }
4678 
4687  public static function getMessageKeysFor( $code ) {
4688  return self::getLocalisationCache()->getSubitemList( $code, 'messages' );
4689  }
4690 
4695  function fixVariableInNamespace( $talk ) {
4696  if ( strpos( $talk, '$1' ) === false ) {
4697  return $talk;
4698  }
4699 
4700  global $wgMetaNamespace;
4701  $talk = str_replace( '$1', $wgMetaNamespace, $talk );
4702 
4703  # Allow grammar transformations
4704  # Allowing full message-style parsing would make simple requests
4705  # such as action=raw much more expensive than they need to be.
4706  # This will hopefully cover most cases.
4707  $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
4708  [ $this, 'replaceGrammarInNamespace' ], $talk );
4709  return str_replace( ' ', '_', $talk );
4710  }
4711 
4716  function replaceGrammarInNamespace( $m ) {
4717  return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
4718  }
4719 
4730  public function formatExpiry( $expiry, $format = true, $infinity = 'infinity' ) {
4731  static $dbInfinity;
4732  if ( $dbInfinity === null ) {
4733  $dbInfinity = wfGetDB( DB_REPLICA )->getInfinity();
4734  }
4735 
4736  if ( $expiry == '' || $expiry === 'infinity' || $expiry == $dbInfinity ) {
4737  return $format === true
4738  ? $this->getMessageFromDB( 'infiniteblock' )
4739  : $infinity;
4740  } else {
4741  return $format === true
4742  ? $this->timeanddate( $expiry, /* User preference timezone */ true )
4743  : wfTimestamp( $format, $expiry );
4744  }
4745  }
4746 
4760  function formatTimePeriod( $seconds, $format = [] ) {
4761  if ( !is_array( $format ) ) {
4762  $format = [ 'avoid' => $format ]; // For backwards compatibility
4763  }
4764  if ( !isset( $format['avoid'] ) ) {
4765  $format['avoid'] = false;
4766  }
4767  if ( !isset( $format['noabbrevs'] ) ) {
4768  $format['noabbrevs'] = false;
4769  }
4770  $secondsMsg = wfMessage(
4771  $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this );
4772  $minutesMsg = wfMessage(
4773  $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this );
4774  $hoursMsg = wfMessage(
4775  $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this );
4776  $daysMsg = wfMessage(
4777  $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this );
4778 
4779  if ( round( $seconds * 10 ) < 100 ) {
4780  $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) );
4781  $s = $secondsMsg->params( $s )->text();
4782  } elseif ( round( $seconds ) < 60 ) {
4783  $s = $this->formatNum( round( $seconds ) );
4784  $s = $secondsMsg->params( $s )->text();
4785  } elseif ( round( $seconds ) < 3600 ) {
4786  $minutes = floor( $seconds / 60 );
4787  $secondsPart = round( fmod( $seconds, 60 ) );
4788  if ( $secondsPart == 60 ) {
4789  $secondsPart = 0;
4790  $minutes++;
4791  }
4792  $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4793  $s .= ' ';
4794  $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4795  } elseif ( round( $seconds ) <= 2 * 86400 ) {
4796  $hours = floor( $seconds / 3600 );
4797  $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4798  $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4799  if ( $secondsPart == 60 ) {
4800  $secondsPart = 0;
4801  $minutes++;
4802  }
4803  if ( $minutes == 60 ) {
4804  $minutes = 0;
4805  $hours++;
4806  }
4807  $s = $hoursMsg->params( $this->formatNum( $hours ) )->text();
4808  $s .= ' ';
4809  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4810  if ( !in_array( $format['avoid'], [ 'avoidseconds', 'avoidminutes' ] ) ) {
4811  $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4812  }
4813  } else {
4814  $days = floor( $seconds / 86400 );
4815  if ( $format['avoid'] === 'avoidminutes' ) {
4816  $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4817  if ( $hours == 24 ) {
4818  $hours = 0;
4819  $days++;
4820  }
4821  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4822  $s .= ' ';
4823  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4824  } elseif ( $format['avoid'] === 'avoidseconds' ) {
4825  $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4826  $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4827  if ( $minutes == 60 ) {
4828  $minutes = 0;
4829  $hours++;
4830  }
4831  if ( $hours == 24 ) {
4832  $hours = 0;
4833  $days++;
4834  }
4835  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4836  $s .= ' ';
4837  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4838  $s .= ' ';
4839  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4840  } else {
4841  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4842  $s .= ' ';
4843  $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format );
4844  }
4845  }
4846  return $s;
4847  }
4848 
4860  function formatBitrate( $bps ) {
4861  return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" );
4862  }
4863 
4870  function formatComputingNumbers( $size, $boundary, $messageKey ) {
4871  if ( $size <= 0 ) {
4872  return str_replace( '$1', $this->formatNum( $size ),
4873  $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) )
4874  );
4875  }
4876  $sizes = [ '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' ];
4877  $index = 0;
4878 
4879  $maxIndex = count( $sizes ) - 1;
4880  while ( $size >= $boundary && $index < $maxIndex ) {
4881  $index++;
4882  $size /= $boundary;
4883  }
4884 
4885  // For small sizes no decimal places necessary
4886  $round = 0;
4887  if ( $index > 1 ) {
4888  // For MB and bigger two decimal places are smarter
4889  $round = 2;
4890  }
4891  $msg = str_replace( '$1', $sizes[$index], $messageKey );
4892 
4893  $size = round( $size, $round );
4894  $text = $this->getMessageFromDB( $msg );
4895  return str_replace( '$1', $this->formatNum( $size ), $text );
4896  }
4897 
4908  function formatSize( $size ) {
4909  return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" );
4910  }
4911 
4921  function specialList( $page, $details, $oppositedm = true ) {
4922  if ( !$details ) {
4923  return $page;
4924  }
4925 
4926  $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) . $this->getDirMark();
4927  return $page .
4928  $dirmark .
4929  $this->msg( 'word-separator' )->escaped() .
4930  $this->msg( 'parentheses' )->rawParams( $details )->escaped();
4931  }
4932 
4943  public function viewPrevNext( Title $title, $offset, $limit,
4944  array $query = [], $atend = false
4945  ) {
4946  // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
4947 
4948  # Make 'previous' link
4949  $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4950  if ( $offset > 0 ) {
4951  $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit,
4952  $query, $prev, 'prevn-title', 'mw-prevlink' );
4953  } else {
4954  $plink = htmlspecialchars( $prev );
4955  }
4956 
4957  # Make 'next' link
4958  $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4959  if ( $atend ) {
4960  $nlink = htmlspecialchars( $next );
4961  } else {
4962  $nlink = $this->numLink( $title, $offset + $limit, $limit,
4963  $query, $next, 'nextn-title', 'mw-nextlink' );
4964  }
4965 
4966  # Make links to set number of items per page
4967  $numLinks = [];
4968  foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
4969  $numLinks[] = $this->numLink( $title, $offset, $num,
4970  $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' );
4971  }
4972 
4973  return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title
4974  )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped();
4975  }
4976 
4989  private function numLink( Title $title, $offset, $limit, array $query, $link,
4990  $tooltipMsg, $class
4991  ) {
4992  $query = [ 'limit' => $limit, 'offset' => $offset ] + $query;
4993  $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
4994  ->numParams( $limit )->text();
4995 
4996  return Html::element( 'a', [ 'href' => $title->getLocalURL( $query ),
4997  'title' => $tooltip, 'class' => $class ], $link );
4998  }
4999 
5005  public function getConvRuleTitle() {
5006  return $this->mConverter->getConvRuleTitle();
5007  }
5008 
5014  public function getCompiledPluralRules() {
5015  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
5016  $fallbacks = self::getFallbacksFor( $this->mCode );
5017  if ( !$pluralRules ) {
5018  foreach ( $fallbacks as $fallbackCode ) {
5019  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
5020  if ( $pluralRules ) {
5021  break;
5022  }
5023  }
5024  }
5025  return $pluralRules;
5026  }
5027 
5033  public function getPluralRules() {
5034  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
5035  $fallbacks = self::getFallbacksFor( $this->mCode );
5036  if ( !$pluralRules ) {
5037  foreach ( $fallbacks as $fallbackCode ) {
5038  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
5039  if ( $pluralRules ) {
5040  break;
5041  }
5042  }
5043  }
5044  return $pluralRules;
5045  }
5046 
5052  public function getPluralRuleTypes() {
5053  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
5054  $fallbacks = self::getFallbacksFor( $this->mCode );
5055  if ( !$pluralRuleTypes ) {
5056  foreach ( $fallbacks as $fallbackCode ) {
5057  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
5058  if ( $pluralRuleTypes ) {
5059  break;
5060  }
5061  }
5062  }
5063  return $pluralRuleTypes;
5064  }
5065 
5071  public function getPluralRuleIndexNumber( $number ) {
5072  $pluralRules = $this->getCompiledPluralRules();
5073  $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5074  return $form;
5075  }
5076 
5085  public function getPluralRuleType( $number ) {
5086  $index = $this->getPluralRuleIndexNumber( $number );
5087  $pluralRuleTypes = $this->getPluralRuleTypes();
5088  if ( isset( $pluralRuleTypes[$index] ) ) {
5089  return $pluralRuleTypes[$index];
5090  } else {
5091  return 'other';
5092  }
5093  }
5094 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1818
Language\$mCode
$mCode
Definition: Language.php:60
Language\internalUserTimeAndDate
internalUserTimeAndDate( $type, $ts, User $user, array $options)
Internal helper function for userDate(), userTime() and userTimeAndDate()
Definition: Language.php:2432
Language\getCodeFromFileName
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
Definition: Language.php:4513
Language\getExtraHashOptions
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
Definition: Language.php:4357
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=self::AS_AUTONYMS, $include=self::ALL)
Definition: Language.php:940
Language\lc
lc( $str, $first=false)
Definition: Language.php:2758
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
Language\parseFormattedNumber
parseFormattedNumber( $number)
Definition: Language.php:3346
Language\getURLVariant
getURLVariant()
Definition: Language.php:4331
Language\getVariantname
getVariantname( $code, $usemsg=true)
short names for language variants used for language conversion links.
Definition: Language.php:769
$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:244
Language\replaceGrammarInNamespace
replaceGrammarInNamespace( $m)
Definition: Language.php:4716
Language\linkPrefixExtension
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
Definition: Language.php:3196
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:1150
Language\clearCaches
static clearCaches()
Intended for tests that may change configuration in a way that invalidates caches.
Definition: Language.php:283
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:3864
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:3493
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:3506
Language\$IRANIAN_DAYS
static $IRANIAN_DAYS
Definition: Language.php:1620
Language\$mIranianCalendarMonthMsgs
static $mIranianCalendarMonthMsgs
Definition: Language.php:119
Language\getIranianCalendarMonthName
getIranianCalendarMonthName( $key)
Definition: Language.php:1036
Language\$mMonthAbbrevMsgs
static $mMonthAbbrevMsgs
Definition: Language.php:114
Language\formatSize
formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB, GB, TB, PB,...
Definition: Language.php:4908
Language\separatorTransformTable
separatorTransformTable()
Definition: Language.php:3450
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
Language\resetNamespaces
resetNamespaces()
Resets all of the namespace caches.
Definition: Language.php:560
Language\getConverter
getConverter()
Return the LanguageConverter used in the Language.
Definition: Language.php:4199
Language\hasVariants
hasVariants()
Check if this is a language with variants.
Definition: Language.php:4267
Language\$mWeekdayAbbrevMsgs
static $mWeekdayAbbrevMsgs
Definition: Language.php:100
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:4086
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
Language\minimumGroupingDigits
minimumGroupingDigits()
Definition: Language.php:3457
Language\segmentByWord
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
Definition: Language.php:2892
Language\iconv
iconv( $in, $out, $string)
Definition: Language.php:2659
Language\getMessageFromDB
getMessageFromDB( $msg)
Get a message from the MediaWiki namespace.
Definition: Language.php:956
captcha-old.count
count
Definition: captcha-old.py:249
Language\$GREG_DAYS
static $GREG_DAYS
Definition: Language.php:1619
Language\convert
convert( $text)
convert text to different variants of a language.
Definition: Language.php:4236
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:3542
Language\ALL
const ALL
Return all known languages in fetchLanguageName(s).
Definition: Language.php:46
Language\updateConversionTable
updateConversionTable(Title $title)
Refresh the cache of conversion tables when MediaWiki:Conversiontable* is updated.
Definition: Language.php:4378
Language\$languageNameCache
static HashBagOStuff null $languageNameCache
Cache for language names.
Definition: Language.php:183
Language\convertGrammar
convertGrammar( $word, $case)
Grammatical transformations, needed for inflected languages Invoked by putting {{grammar:case|word}} ...
Definition: Language.php:3886
$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:1954
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:964
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:3586
Language\$mMonthGenMsgs
static $mMonthGenMsgs
Definition: Language.php:109
Language\timeanddate
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2336
MediaWikiTitleCodec\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: MediaWikiTitleCodec.php:460
Language\getHebrewCalendarMonthNameGen
getHebrewCalendarMonthNameGen( $key)
Definition: Language.php:1052
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\$mMagicHookDone
$mMagicHookDone
Definition: Language.php:61
Language\$mConverter
LanguageConverter $mConverter
Definition: Language.php:58
$wgLangObjCacheSize
$wgLangObjCacheSize
Language cache size, or really how many languages can we handle simultaneously without degrading to c...
Definition: DefaultSettings.php:2948
Language\getMonthNamesArray
getMonthNamesArray()
Definition: Language.php:981
Language\MESSAGES_FALLBACKS
const MESSAGES_FALLBACKS
Return a fallback chain for messages in getFallbacksFor.
Definition: Language.php:87
$s
$s
Definition: mergeMessageFileList.php:187
Language\$mHijriCalendarMonthMsgs
static $mHijriCalendarMonthMsgs
Definition: Language.php:142
Language\$namespaceNames
array null $namespaceNames
Definition: Language.php:68
Language\getPreferredVariant
getPreferredVariant()
Definition: Language.php:4317
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1145
Language\getMonthAbbreviation
getMonthAbbreviation( $key)
Definition: Language.php:1001
Language\ucwordbreaksCallbackAscii
ucwordbreaksCallbackAscii( $matches)
Definition: Language.php:2676
$digitGroupingPattern
$digitGroupingPattern
Definition: MessagesAs.php:167
Language\recodeForEdit
recodeForEdit( $s)
Definition: Language.php:3022
Language\markNoConversion
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
Definition: Language.php:4398
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:3495
Language\getDefaultVariant
getDefaultVariant()
Definition: Language.php:4324
Language\fallback8bitEncoding
fallback8bitEncoding()
Definition: Language.php:2869
Language\embedBidi
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
Definition: Language.php:4109
Language\formatTimePeriod
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
Definition: Language.php:4760
Language\formatBitrate
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
Definition: Language.php:4860
Language\equals
equals(Language $lang)
Compare with an other language object.
Definition: Language.php:4462
Language\SUPPORTED
const SUPPORTED
Return in fetchLanguageName(s) only the languages for which we have at least some localisation.
Definition: Language.php:53
Language\getSpecialPageAliases
getSpecialPageAliases()
Get special page names, as an associative array canonical name => array of valid names,...
Definition: Language.php:3265
Language\getMessagesFileName
static getMessagesFileName( $code)
Definition: Language.php:4556
Language\getPluralRuleTypes
getPluralRuleTypes()
Get the plural rule types for the language.
Definition: Language.php:5052
Language\capitalizeAllNouns
capitalizeAllNouns()
Definition: Language.php:3163
Language\AS_AUTONYMS
const AS_AUTONYMS
Return autonyms in fetchLanguageName(s).
Definition: Language.php:40
Language\specialList
specialList( $page, $details, $oppositedm=true)
Make a list item, used by various special pages.
Definition: Language.php:4921
Language\$mHebrewCalendarMonthGenMsgs
static $mHebrewCalendarMonthGenMsgs
Definition: Language.php:134
$wgMetaNamespace
$wgMetaNamespace
Name of the project namespace.
Definition: DefaultSettings.php:3839
Language\checkTitleEncoding
checkTitleEncoding( $s)
Definition: Language.php:2855
Language\$mParentLanguage
$mParentLanguage
Definition: Language.php:62
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:35
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:332
Language\getFormattedNamespaces
getFormattedNamespaces()
A convenience function that returns getNamespaces() with spaces instead of underscores in values.
Definition: Language.php:572
Language\lcfirst
lcfirst( $str)
Definition: Language.php:2739
Language\initEncoding
initEncoding()
Definition: Language.php:3012
Language\formatExpiry
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
Definition: Language.php:4730
Language\isKnownLanguageTag
static isKnownLanguageTag( $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
Definition: Language.php:433
Language\emphasize
emphasize( $text)
Italic is unsuitable for some languages.
Definition: Language.php:3284
Language\getFallbackLanguages
getFallbackLanguages()
Definition: Language.php:494
Language\getDurationIntervals
getDurationIntervals( $seconds, array $chosenIntervals=[])
Takes a number of seconds and returns an array with a set of corresponding intervals.
Definition: Language.php:2381
Language\caseFold
caseFold( $s)
Return a case-folded representation of $s.
Definition: Language.php:2846
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:2517
Language\segmentForDiff
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
Definition: Language.php:4179
Language\classFromCode
static classFromCode( $code, $fallback=true)
Definition: Language.php:4528
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:267
Language\getWeekdayAbbreviation
getWeekdayAbbreviation( $key)
Definition: Language.php:1028
Language\msg
msg( $msg)
Get message object in this language.
Definition: Language.php:966
$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:1627
Language\needsGenderDistinction
needsGenderDistinction()
Whether this language uses gender-dependent namespace aliases.
Definition: Language.php:637
Language\getParsedTitle
getParsedTitle()
For languages that support multiple variants, the title of an article may be displayed differently in...
Definition: Language.php:4368
Language\dateTimeObjFormat
static dateTimeObjFormat(&$dateTimeObj, $ts, $zone, $code)
Pass through result from $dateTimeObj->format()
Definition: Language.php:1072
FormatJson\decode
static decode( $value, $assoc=false)
Decodes a JSON string.
Definition: FormatJson.php:164
Language\getLocalisationCache
static getLocalisationCache()
Get the LocalisationCache instance.
Definition: Language.php:454
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:68
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:4943
Language\$transformData
$transformData
ReplacementArray object caches.
Definition: Language.php:74
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1118
Language\tsToIranian
static tsToIranian( $ts)
Algorithm by Roozbeh Pournader and Mohammad Toossi to convert Gregorian dates to Iranian dates.
Definition: Language.php:1634
Language\unsegmentForDiff
unsegmentForDiff( $text)
and unsegment to show the result
Definition: Language.php:4189
Language\autoConvert
autoConvert( $text, $variant=false)
convert text to a variant
Definition: Language.php:4211
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:3723
Language\normalize
normalize( $s)
Convert a UTF-8 string to normal form C.
Definition: Language.php:3048
Language\getBookstoreList
getBookstoreList()
Exports $wgBookstoreListEn.
Definition: Language.php:502
Language\$mHebrewCalendarMonthMsgs
static $mHebrewCalendarMonthMsgs
Definition: Language.php:126
Language\pipeList
pipeList(array $list)
Same as commaList, but separate it with the pipe instead.
Definition: Language.php:3518
Language\$grammarTransformations
static MapCacheLRU null $grammarTransformations
Cache for grammar rules data.
Definition: Language.php:177
Language\doMagicHook
doMagicHook()
Run the LanguageGetMagic hook once.
Definition: Language.php:3211
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
Language\convertForSearchResult
convertForSearchResult( $termsArray)
Definition: Language.php:2945
wfUrlProtocolsWithoutProtRel
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
Definition: GlobalFunctions.php:785
Language\getMessageFor
static getMessageFor( $key, $code)
Get a message for a given language.
Definition: Language.php:4675
$matches
$matches
Definition: NoLocalSettings.php:24
Language\getGenderNsText
getGenderNsText( $index, $gender)
Returns gender-dependent namespace alias if available.
Definition: Language.php:622
Language\listToText
listToText(array $list)
Take a list of strings and build a locale-friendly comma-separated list, using the local comma-separa...
Definition: Language.php:3469
not
if not
Definition: COPYING.txt:307
Language\getHtmlCode
getHtmlCode()
Get the code in BCP 47 format which we can use inside of html lang="" tags.
Definition: Language.php:4488
Language\getNamespaceAliases
getNamespaceAliases()
Definition: Language.php:670
Language\getLocalNsIndex
getLocalNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:661
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:2471
MediaWiki
A helper class for throttling authentication attempts.
$IP
$IP
Definition: update.php:3
Language\addMagicWordsByLang
addMagicWordsByLang( $newWords)
Add magic words to the extension array.
Definition: Language.php:3250
Language\getMonthName
getMonthName( $key)
Definition: Language.php:974
Language\$mMonthMsgs
static $mMonthMsgs
Definition: Language.php:104
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:3562
Language\getAllMessages
getAllMessages()
Definition: Language.php:2649
Language\getMagic
getMagic( $mw)
Fill a MagicWord object with data from here.
Definition: Language.php:3224
MapCacheLRU
Handles a simple LRU key/value map with a maximum number of entries.
Definition: MapCacheLRU.php:37
TO
we sometimes make exceptions for this Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally NO WARRANTY BECAUSE THE PROGRAM IS LICENSED FREE OF THERE IS NO WARRANTY FOR THE TO THE EXTENT PERMITTED BY APPLICABLE LAW EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND OR OTHER PARTIES PROVIDE THE PROGRAM AS IS WITHOUT WARRANTY OF ANY EITHER EXPRESSED OR BUT NOT LIMITED TO
Definition: COPYING.txt:260
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$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:813
Language\$rle
static $rle
Definition: Language.php:189
Language\isRTL
isRTL()
For right-to-left language support.
Definition: Language.php:3087
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:2494
$wgLocalisationCacheConf
$wgLocalisationCacheConf
Localisation cache configuration.
Definition: DefaultSettings.php:2600
Language\getNsIndex
getNsIndex( $text)
Get a namespace key by value, case insensitive.
Definition: Language.php:752
Language\getNamespaceIds
getNamespaceIds()
Definition: Language.php:722
Language\getPluralRuleIndexNumber
getPluralRuleIndexNumber( $number)
Find the index number of the plural rule appropriate for the given number.
Definition: Language.php:5071
Language\getMonthNameGen
getMonthNameGen( $key)
Definition: Language.php:993
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1841
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:2915
Language\getCompiledPluralRules
getCompiledPluralRules()
Get the compiled plural rules for the language.
Definition: Language.php:5014
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:2573
Language\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:4300
Language\digitGroupingPattern
digitGroupingPattern()
Definition: Language.php:3436
Language\ucwords
ucwords( $str)
Definition: Language.php:2782
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
Language\initContLang
initContLang()
Hook which will be called if this is the content language.
Definition: Language.php:487
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:175
Language\getPluralRules
getPluralRules()
Get the plural rules for the language.
Definition: Language.php:5033
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:3174
Language\uc
uc( $str, $first=false)
Convert a string to uppercase.
Definition: Language.php:2723
Language\$mVariants
$mVariants
Definition: Language.php:60
Language\translateBlockExpiry
translateBlockExpiry( $str, User $user=null, $now=0)
Definition: Language.php:4136
or
or
Definition: COPYING.txt:140
Language\formatNumNoSeparators
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
Definition: Language.php:3338
$wgTranslateNumerals
$wgTranslateNumerals
For Hindi and Arabic use local numerals instead of Western style (0-9) numerals in interface.
Definition: DefaultSettings.php:3090
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Language\truncateInternal
truncateInternal( $string, $length, $ellipsis, $adjustLength, $measureLength, $getSubstring)
Internal method used for truncation.
Definition: Language.php:3612
Language\$mHtmlCode
$mHtmlCode
Definition: Language.php:62
$wgNamespaceAliases
$wgNamespaceAliases
Namespace aliases.
Definition: DefaultSettings.php:3904
Language\$mLoaded
$mLoaded
Definition: Language.php:60
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:411
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:2355
Language\setNamespaces
setNamespaces(array $namespaces)
Arbitrarily set all of the namespace names at once.
Definition: Language.php:552
Language\linkPrefixCharset
linkPrefixCharset()
A regular expression character set to match legal word-prefixing characters which should be merged on...
Definition: Language.php:4424
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2213
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:4347
Language\fixVariableInNamespace
fixVariableInNamespace( $talk)
Definition: Language.php:4695
$value
$value
Definition: styleTest.css.php:49
Language\ucwordbreaks
ucwordbreaks( $str)
capitalize words at word breaks
Definition: Language.php:2806
Language\getNsText
getNsText( $index)
Get a namespace value by key.
Definition: Language.php:591
$wgLocalTZoffset
$wgLocalTZoffset
Set an offset from UTC in minutes to use for the default timezone setting for anonymous users and new...
Definition: DefaultSettings.php:3221
Language\getCode
getCode()
Get the internal language code for this language object.
Definition: Language.php:4474
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:5085
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:2232
$wgMetaNamespaceTalk
$wgMetaNamespaceTalk
Name of the project talk namespace.
Definition: DefaultSettings.php:3848
Language\ucwordsCallbackMB
ucwordsCallbackMB( $matches)
Definition: Language.php:2692
$wgLanguageCode
$wgLanguageCode
Site language code.
Definition: DefaultSettings.php:2942
Language\hebrewNumeral
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
Definition: Language.php:2067
wfIsInfinity
wfIsInfinity( $str)
Determine input string is represents as infinity.
Definition: GlobalFunctions.php:3176
Language\getFormattedNsText
getFormattedNsText( $index)
A convenience function that returns the same thing as getNsText() except with '_' changed to ' ',...
Definition: Language.php:609
Language\getMessagesFor
static getMessagesFor( $code)
Get all messages for a given language WARNING: this may take a long time.
Definition: Language.php:4663
Language\ucwordbreaksCallbackMB
ucwordbreaksCallbackMB( $matches)
Definition: Language.php:2684
NS_PROJECT_TALK
const NS_PROJECT_TALK
Definition: Defines.php:69
Language\handleExplicitPluralForms
handleExplicitPluralForms( $count, array $forms)
Handles explicit plural forms for Language::convertPlural()
Definition: Language.php:4065
$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:2036
Language\getFileName
static getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: Language.php:4544
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:3032
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:432
Language\getDirMark
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:3151
Language\normalizeForSearch
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
Definition: Language.php:2903
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:386
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:2154
Language\formatComputingNumbers
formatComputingNumbers( $size, $boundary, $messageKey)
Definition: Language.php:4870
Language\convertHtml
convertHtml( $text, $isTitle=false)
Perform output conversion on a string, and encode for safe HTML output.
Definition: Language.php:4292
Language\getFallbackFor
static getFallbackFor( $code)
Get the first fallback for a given language.
Definition: Language.php:4586
Language\__construct
__construct()
Definition: Language.php:463
Language\getMonthAbbreviationsArray
getMonthAbbreviationsArray()
Definition: Language.php:1008
Language\getParentLanguage
getParentLanguage()
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
Definition: Language.php:4435
Language\$dateFormatStrings
$dateFormatStrings
Definition: Language.php:64
Language\hasWordBreaks
hasWordBreaks()
Most writing systems use whitespace to break up words.
Definition: Language.php:2881
Language\getDir
getDir()
Return the correct HTML 'dir' attribute value for this language.
Definition: Language.php:3095
Language\date
date( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2297
Language\alignEnd
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
Definition: Language.php:3119
Title
Represents a title within MediaWiki.
Definition: Title.php:39
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:3841
Language\setCode
setCode( $code)
Definition: Language.php:4499
Language\getDefaultDateFormat
getDefaultDateFormat()
Definition: Language.php:800
Language\getVariants
getVariants()
Get the list of variants supported by this language see sample implementation in LanguageZh....
Definition: Language.php:4310
Language\convertPlural
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
Definition: Language.php:4035
$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
$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:2036
Language\$mLangObjCache
static $mLangObjCache
Definition: Language.php:81
$minimumGroupingDigits
$minimumGroupingDigits
Definition: MessagesBe_tarask.php:239
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:4632
$wgGrammarForms
$wgGrammarForms
Some languages need different word forms, usually for different cases.
Definition: DefaultSettings.php:2959
Language\getGrammarForms
getGrammarForms()
Get the grammar forms for the content language.
Definition: Language.php:3937
Language\removeBadCharFirst
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
Definition: Language.php:3697
Language\$strongDirRegex
static $strongDirRegex
Directionality test regex for embedBidi().
Definition: Language.php:205
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:9
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:182
$wgExtraGenderNamespaces
$wgExtraGenderNamespaces
Same as above, but for namespaces with gender distinction.
Definition: DefaultSettings.php:3884
Language\getHijriCalendarMonthName
getHijriCalendarMonthName( $key)
Definition: Language.php:1060
Language\getMessage
getMessage( $key)
Definition: Language.php:2642
Language\getGrammarTransformations
getGrammarTransformations()
Get the grammar transformations data for the language.
Definition: Language.php:3957
Language\getMessageKeysFor
static getMessageKeysFor( $code)
Get all message keys for a given language.
Definition: Language.php:4687
Language\getJsonMessagesFileName
static getJsonMessagesFileName( $code)
Definition: Language.php:4569
Language\firstChar
firstChar( $s)
Get the first character of a string.
Definition: Language.php:2957
NS_USER
const NS_USER
Definition: Defines.php:66
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3090
Language\getNamespaces
getNamespaces()
Returns an array of localised namespaces indexed by their numbers.
Definition: Language.php:512
SpecialBlock\getSuggestedDurations
static getSuggestedDurations(Language $lang=null, $includeOther=true)
Get an array of suggested block durations from MediaWiki:Ipboptions.
Definition: SpecialBlock.php:861
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:3073
Language\removeBadCharLast
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
Definition: Language.php:3671
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:2036
Language\romanNumeral
static romanNumeral( $num)
Roman number formatting up to 10000.
Definition: Language.php:2036
Language\getConvRuleTitle
getConvRuleTitle()
Get the conversion rule title, if any.
Definition: Language.php:5005
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
Definition: globals.txt:10
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:214
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:1132
Language\$namespaceAliases
$namespaceAliases
Definition: Language.php:69
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:52
Language\tsToHijri
static tsToHijri( $ts)
Converting Gregorian dates to Hijri dates.
Definition: Language.php:1694
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:2536
$t
$t
Definition: testCompression.php:69
Language\time
time( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2316
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
Language\insertSpace
static insertSpace( $string, $pattern)
Definition: Language.php:2935
Language\isMultibyte
isMultibyte( $str)
Definition: Language.php:2774
Language\alignStart
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
Definition: Language.php:3107
Language\isSupportedLanguage
static isSupportedLanguage( $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
Definition: Language.php:304
Language\$mWeekdayMsgs
static $mWeekdayMsgs
Definition: Language.php:95
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=self::AS_AUTONYMS, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:843
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 use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
Language\digitTransformTable
digitTransformTable()
Definition: Language.php:3443
Language\tsToYear
static tsToYear( $ts, $cName)
Algorithm to convert Gregorian dates to Thai solar dates, Minguo dates or Minguo dates.
Definition: Language.php:1925
Language\getDirMarkEntity
getDirMarkEntity( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:3134
options
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
Language\strongDirFromContent
static strongDirFromContent( $text='')
Gets directionality of the first strongly directional codepoint, for embedBidi()
Definition: Language.php:2019
Language\hebrewYearStart
static hebrewYearStart( $year)
This calculates the Hebrew year start, as days since 1 September.
Definition: Language.php:1887
Language\$mNamespaceIds
$mNamespaceIds
Definition: Language.php:69
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:47
Language\getDateFormatString
getDateFormatString( $type, $pref)
Get a format string for a given type and preference.
Definition: Language.php:2263
Language\$dataCache
static LocalisationCache $dataCache
Definition: Language.php:79
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
Language\getMagicWords
getMagicWords()
Get all magic words from cache.
Definition: Language.php:3204
Language\hasVariant
hasVariant( $variant)
Strict check if the language has the specific variant.
Definition: Language.php:4281
Language\numLink
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
Definition: Language.php:4989
Language\__destruct
__destruct()
Reduce memory usage.
Definition: Language.php:477
Language\commafy
commafy( $number)
Adds commas to a given number.
Definition: Language.php:3371
Language\$pdf
static $pdf
Definition: Language.php:190
Language\STRICT_FALLBACKS
const STRICT_FALLBACKS
Return a strict fallback chain in getFallbacksFor.
Definition: Language.php:93
$wgExtraNamespaces
$wgExtraNamespaces
Additional namespaces.
Definition: DefaultSettings.php:3876
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:239
Language\getFallbacksFor
static getFallbacksFor( $code, $mode=self::MESSAGES_FALLBACKS)
Get the ordered list of fallback languages.
Definition: Language.php:4604
Language\$mExtendedSpecialPageAliases
$mExtendedSpecialPageAliases
Definition: Language.php:65
Language\$durationIntervals
static array $durationIntervals
Definition: Language.php:153
Language\tsToHebrew
static tsToHebrew( $ts)
Converting Gregorian dates to Hebrew dates.
Definition: Language.php:1746
Language\autoConvertToAllVariants
autoConvertToAllVariants( $text)
convert text to all supported variants
Definition: Language.php:4221
Language\linkTrail
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
Definition: Language.php:4414
$wgAllUnicodeFixes
$wgAllUnicodeFixes
Set this to always convert certain Unicode sequences to modern ones regardless of the content languag...
Definition: DefaultSettings.php:3052
Language\ucfirst
ucfirst( $str)
Make a string's first character uppercase.
Definition: Language.php:2703
Language\$mMagicExtensions
$mMagicExtensions
Definition: Language.php:61
$wgDummyLanguageCodes
$wgDummyLanguageCodes
Functionally the same as $wgExtraLanguageCodes, but deprecated.
Definition: DefaultSettings.php:3019
Language\getWeekdayName
getWeekdayName( $key)
Definition: Language.php:1020
Language\gender
gender( $gender, $forms)
Provides an alternative text depending on specified gender.
Definition: Language.php:4006
Language\$lre
static $lre
Unicode directional formatting characters, for embedBidi()
Definition: Language.php:188
Language\getHebrewCalendarMonthName
getHebrewCalendarMonthName( $key)
Definition: Language.php:1044
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:3310
$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:813
$type
$type
Definition: testCompression.php:48
Language\$fallbackLanguageCache
static array $fallbackLanguageCache
Cache for language fallbacks.
Definition: Language.php:171
Language\convertNamespace
convertNamespace( $ns, $variant=null)
Convert a namespace index to a string in the preferred variant.
Definition: Language.php:4258
Language\convertTitle
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
Definition: Language.php:4246