MediaWiki  1.32.5
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  } elseif (
1991  ( ( $gy == 1989 ) && ( $gm == 1 ) && ( $gd >= 8 ) ) ||
1992  ( ( $gy > 1989 ) && ( $gy < 2019 ) ) ||
1993  ( ( $gy == 2019 ) && ( $gm < 5 ) )
1994  ) {
1995  # Heisei period
1996  $gy_gannen = $gy - 1989 + 1;
1997  $gy_offset = $gy_gannen;
1998  if ( $gy_gannen == 1 ) {
1999  $gy_offset = '元';
2000  }
2001  $gy_offset = '平成' . $gy_offset;
2002  } else {
2003  # Reiwa period
2004  $gy_gannen = $gy - 2019 + 1;
2005  $gy_offset = $gy_gannen;
2006  if ( $gy_gannen == 1 ) {
2007  $gy_offset = '元';
2008  }
2009  $gy_offset = '令和' . $gy_offset;
2010  }
2011  } else {
2012  $gy_offset = $gy;
2013  }
2014 
2015  return [ $gy_offset, $gm, $gd ];
2016  }
2017 
2031  private static function strongDirFromContent( $text = '' ) {
2032  if ( !preg_match( self::$strongDirRegex, $text, $matches ) ) {
2033  return null;
2034  }
2035  if ( $matches[1] === '' ) {
2036  return 'rtl';
2037  }
2038  return 'ltr';
2039  }
2040 
2048  static function romanNumeral( $num ) {
2049  static $table = [
2050  [ '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X' ],
2051  [ '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', 'C' ],
2052  [ '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', 'M' ],
2053  [ '', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM', 'MMMMMM', 'MMMMMMM',
2054  'MMMMMMMM', 'MMMMMMMMM', 'MMMMMMMMMM' ]
2055  ];
2056 
2057  $num = intval( $num );
2058  if ( $num > 10000 || $num <= 0 ) {
2059  return $num;
2060  }
2061 
2062  $s = '';
2063  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2064  if ( $num >= $pow10 ) {
2065  $s .= $table[$i][(int)floor( $num / $pow10 )];
2066  }
2067  $num = $num % $pow10;
2068  }
2069  return $s;
2070  }
2071 
2079  static function hebrewNumeral( $num ) {
2080  static $table = [
2081  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ],
2082  [ '', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק' ],
2083  [ '',
2084  [ 'ק' ],
2085  [ 'ר' ],
2086  [ 'ש' ],
2087  [ 'ת' ],
2088  [ 'ת', 'ק' ],
2089  [ 'ת', 'ר' ],
2090  [ 'ת', 'ש' ],
2091  [ 'ת', 'ת' ],
2092  [ 'ת', 'ת', 'ק' ],
2093  [ 'ת', 'ת', 'ר' ],
2094  ],
2095  [ '', 'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י' ]
2096  ];
2097 
2098  $num = intval( $num );
2099  if ( $num > 9999 || $num <= 0 ) {
2100  return $num;
2101  }
2102 
2103  // Round thousands have special notations
2104  if ( $num === 1000 ) {
2105  return "א' אלף";
2106  } elseif ( $num % 1000 === 0 ) {
2107  return $table[0][$num / 1000] . "' אלפים";
2108  }
2109 
2110  $letters = [];
2111 
2112  for ( $pow10 = 1000, $i = 3; $i >= 0; $pow10 /= 10, $i-- ) {
2113  if ( $num >= $pow10 ) {
2114  if ( $num === 15 || $num === 16 ) {
2115  $letters[] = $table[0][9];
2116  $letters[] = $table[0][$num - 9];
2117  $num = 0;
2118  } else {
2119  $letters = array_merge(
2120  $letters,
2121  (array)$table[$i][intval( $num / $pow10 )]
2122  );
2123 
2124  if ( $pow10 === 1000 ) {
2125  $letters[] = "'";
2126  }
2127  }
2128  }
2129 
2130  $num = $num % $pow10;
2131  }
2132 
2133  $preTransformLength = count( $letters );
2134  if ( $preTransformLength === 1 ) {
2135  // Add geresh (single quote) to one-letter numbers
2136  $letters[] = "'";
2137  } else {
2138  $lastIndex = $preTransformLength - 1;
2139  $letters[$lastIndex] = str_replace(
2140  [ 'כ', 'מ', 'נ', 'פ', 'צ' ],
2141  [ 'ך', 'ם', 'ן', 'ף', 'ץ' ],
2142  $letters[$lastIndex]
2143  );
2144 
2145  // Add gershayim (double quote) to multiple-letter numbers,
2146  // but exclude numbers with only one letter after the thousands
2147  // (1001-1009, 1020, 1030, 2001-2009, etc.)
2148  if ( $letters[1] === "'" && $preTransformLength === 3 ) {
2149  $letters[] = "'";
2150  } else {
2151  array_splice( $letters, -1, 0, '"' );
2152  }
2153  }
2154 
2155  return implode( $letters );
2156  }
2157 
2166  public function userAdjust( $ts, $tz = false ) {
2167  global $wgUser, $wgLocalTZoffset;
2168 
2169  if ( $tz === false ) {
2170  $tz = $wgUser->getOption( 'timecorrection' );
2171  }
2172 
2173  $data = explode( '|', $tz, 3 );
2174 
2175  if ( $data[0] == 'ZoneInfo' ) {
2176  try {
2177  $userTZ = new DateTimeZone( $data[2] );
2178  $date = new DateTime( $ts, new DateTimeZone( 'UTC' ) );
2179  $date->setTimezone( $userTZ );
2180  return $date->format( 'YmdHis' );
2181  } catch ( Exception $e ) {
2182  // Unrecognized timezone, default to 'Offset' with the stored offset.
2183  $data[0] = 'Offset';
2184  }
2185  }
2186 
2187  if ( $data[0] == 'System' || $tz == '' ) {
2188  # Global offset in minutes.
2189  $minDiff = $wgLocalTZoffset;
2190  } elseif ( $data[0] == 'Offset' ) {
2191  $minDiff = intval( $data[1] );
2192  } else {
2193  $data = explode( ':', $tz );
2194  if ( count( $data ) == 2 ) {
2195  $data[0] = intval( $data[0] );
2196  $data[1] = intval( $data[1] );
2197  $minDiff = abs( $data[0] ) * 60 + $data[1];
2198  if ( $data[0] < 0 ) {
2199  $minDiff = -$minDiff;
2200  }
2201  } else {
2202  $minDiff = intval( $data[0] ) * 60;
2203  }
2204  }
2205 
2206  # No difference ? Return time unchanged
2207  if ( 0 == $minDiff ) {
2208  return $ts;
2209  }
2210 
2211  Wikimedia\suppressWarnings(); // E_STRICT system time bitching
2212  # Generate an adjusted date; take advantage of the fact that mktime
2213  # will normalize out-of-range values so we don't have to split $minDiff
2214  # into hours and minutes.
2215  $t = mktime( (
2216  (int)substr( $ts, 8, 2 ) ), # Hours
2217  (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes
2218  (int)substr( $ts, 12, 2 ), # Seconds
2219  (int)substr( $ts, 4, 2 ), # Month
2220  (int)substr( $ts, 6, 2 ), # Day
2221  (int)substr( $ts, 0, 4 ) ); # Year
2222 
2223  $date = date( 'YmdHis', $t );
2224  Wikimedia\restoreWarnings();
2225 
2226  return $date;
2227  }
2228 
2244  function dateFormat( $usePrefs = true ) {
2245  global $wgUser;
2246 
2247  if ( is_bool( $usePrefs ) ) {
2248  if ( $usePrefs ) {
2249  $datePreference = $wgUser->getDatePreference();
2250  } else {
2251  $datePreference = (string)User::getDefaultOption( 'date' );
2252  }
2253  } else {
2254  $datePreference = (string)$usePrefs;
2255  }
2256 
2257  // return int
2258  if ( $datePreference == '' ) {
2259  return 'default';
2260  }
2261 
2262  return $datePreference;
2263  }
2264 
2275  function getDateFormatString( $type, $pref ) {
2276  $wasDefault = false;
2277  if ( $pref == 'default' ) {
2278  $wasDefault = true;
2279  $pref = $this->getDefaultDateFormat();
2280  }
2281 
2282  if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
2283  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2284 
2285  if ( $type === 'pretty' && $df === null ) {
2286  $df = $this->getDateFormatString( 'date', $pref );
2287  }
2288 
2289  if ( !$wasDefault && $df === null ) {
2290  $pref = $this->getDefaultDateFormat();
2291  $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
2292  }
2293 
2294  $this->dateFormatStrings[$type][$pref] = $df;
2295  }
2296  return $this->dateFormatStrings[$type][$pref];
2297  }
2298 
2309  public function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
2310  $ts = wfTimestamp( TS_MW, $ts );
2311  if ( $adj ) {
2312  $ts = $this->userAdjust( $ts, $timecorrection );
2313  }
2314  $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
2315  return $this->sprintfDate( $df, $ts );
2316  }
2317 
2328  public function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
2329  $ts = wfTimestamp( TS_MW, $ts );
2330  if ( $adj ) {
2331  $ts = $this->userAdjust( $ts, $timecorrection );
2332  }
2333  $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
2334  return $this->sprintfDate( $df, $ts );
2335  }
2336 
2348  public function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false ) {
2349  $ts = wfTimestamp( TS_MW, $ts );
2350  if ( $adj ) {
2351  $ts = $this->userAdjust( $ts, $timecorrection );
2352  }
2353  $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
2354  return $this->sprintfDate( $df, $ts );
2355  }
2356 
2367  public function formatDuration( $seconds, array $chosenIntervals = [] ) {
2368  $intervals = $this->getDurationIntervals( $seconds, $chosenIntervals );
2369 
2370  $segments = [];
2371 
2372  foreach ( $intervals as $intervalName => $intervalValue ) {
2373  // Messages: duration-seconds, duration-minutes, duration-hours, duration-days, duration-weeks,
2374  // duration-years, duration-decades, duration-centuries, duration-millennia
2375  $message = wfMessage( 'duration-' . $intervalName )->numParams( $intervalValue );
2376  $segments[] = $message->inLanguage( $this )->escaped();
2377  }
2378 
2379  return $this->listToText( $segments );
2380  }
2381 
2393  public function getDurationIntervals( $seconds, array $chosenIntervals = [] ) {
2394  if ( empty( $chosenIntervals ) ) {
2395  $chosenIntervals = [
2396  'millennia',
2397  'centuries',
2398  'decades',
2399  'years',
2400  'days',
2401  'hours',
2402  'minutes',
2403  'seconds'
2404  ];
2405  }
2406 
2407  $intervals = array_intersect_key( self::$durationIntervals, array_flip( $chosenIntervals ) );
2408  $sortedNames = array_keys( $intervals );
2409  $smallestInterval = array_pop( $sortedNames );
2410 
2411  $segments = [];
2412 
2413  foreach ( $intervals as $name => $length ) {
2414  $value = floor( $seconds / $length );
2415 
2416  if ( $value > 0 || ( $name == $smallestInterval && empty( $segments ) ) ) {
2417  $seconds -= $value * $length;
2418  $segments[$name] = $value;
2419  }
2420  }
2421 
2422  return $segments;
2423  }
2424 
2444  private function internalUserTimeAndDate( $type, $ts, User $user, array $options ) {
2445  $ts = wfTimestamp( TS_MW, $ts );
2446  $options += [ 'timecorrection' => true, 'format' => true ];
2447  if ( $options['timecorrection'] !== false ) {
2448  if ( $options['timecorrection'] === true ) {
2449  $offset = $user->getOption( 'timecorrection' );
2450  } else {
2451  $offset = $options['timecorrection'];
2452  }
2453  $ts = $this->userAdjust( $ts, $offset );
2454  }
2455  if ( $options['format'] === true ) {
2456  $format = $user->getDatePreference();
2457  } else {
2458  $format = $options['format'];
2459  }
2460  $df = $this->getDateFormatString( $type, $this->dateFormat( $format ) );
2461  return $this->sprintfDate( $df, $ts );
2462  }
2463 
2483  public function userDate( $ts, User $user, array $options = [] ) {
2484  return $this->internalUserTimeAndDate( 'date', $ts, $user, $options );
2485  }
2486 
2506  public function userTime( $ts, User $user, array $options = [] ) {
2507  return $this->internalUserTimeAndDate( 'time', $ts, $user, $options );
2508  }
2509 
2529  public function userTimeAndDate( $ts, User $user, array $options = [] ) {
2530  return $this->internalUserTimeAndDate( 'both', $ts, $user, $options );
2531  }
2532 
2548  public function getHumanTimestamp(
2549  MWTimestamp $time, MWTimestamp $relativeTo = null, User $user = null
2550  ) {
2551  if ( $relativeTo === null ) {
2552  $relativeTo = new MWTimestamp();
2553  }
2554  if ( $user === null ) {
2555  $user = RequestContext::getMain()->getUser();
2556  }
2557 
2558  // Adjust for the user's timezone.
2559  $offsetThis = $time->offsetForUser( $user );
2560  $offsetRel = $relativeTo->offsetForUser( $user );
2561 
2562  $ts = '';
2563  if ( Hooks::run( 'GetHumanTimestamp', [ &$ts, $time, $relativeTo, $user, $this ] ) ) {
2564  $ts = $this->getHumanTimestampInternal( $time, $relativeTo, $user );
2565  }
2566 
2567  // Reset the timezone on the objects.
2568  $time->timestamp->sub( $offsetThis );
2569  $relativeTo->timestamp->sub( $offsetRel );
2570 
2571  return $ts;
2572  }
2573 
2585  private function getHumanTimestampInternal(
2586  MWTimestamp $ts, MWTimestamp $relativeTo, User $user
2587  ) {
2588  $diff = $ts->diff( $relativeTo );
2589  $diffDay = (bool)( (int)$ts->timestamp->format( 'w' ) -
2590  (int)$relativeTo->timestamp->format( 'w' ) );
2591  $days = $diff->days ?: (int)$diffDay;
2592  if ( $diff->invert || $days > 5
2593  && $ts->timestamp->format( 'Y' ) !== $relativeTo->timestamp->format( 'Y' )
2594  ) {
2595  // Timestamps are in different years: use full timestamp
2596  // Also do full timestamp for future dates
2600  $format = $this->getDateFormatString( 'both', $user->getDatePreference() ?: 'default' );
2601  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2602  } elseif ( $days > 5 ) {
2603  // Timestamps are in same year, but more than 5 days ago: show day and month only.
2604  $format = $this->getDateFormatString( 'pretty', $user->getDatePreference() ?: 'default' );
2605  $ts = $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) );
2606  } elseif ( $days > 1 ) {
2607  // Timestamp within the past week: show the day of the week and time
2608  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2609  $weekday = self::$mWeekdayMsgs[$ts->timestamp->format( 'w' )];
2610  // Messages:
2611  // sunday-at, monday-at, tuesday-at, wednesday-at, thursday-at, friday-at, saturday-at
2612  $ts = wfMessage( "$weekday-at" )
2613  ->inLanguage( $this )
2614  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2615  ->text();
2616  } elseif ( $days == 1 ) {
2617  // Timestamp was yesterday: say 'yesterday' and the time.
2618  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2619  $ts = wfMessage( 'yesterday-at' )
2620  ->inLanguage( $this )
2621  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2622  ->text();
2623  } elseif ( $diff->h > 1 || $diff->h == 1 && $diff->i > 30 ) {
2624  // Timestamp was today, but more than 90 minutes ago: say 'today' and the time.
2625  $format = $this->getDateFormatString( 'time', $user->getDatePreference() ?: 'default' );
2626  $ts = wfMessage( 'today-at' )
2627  ->inLanguage( $this )
2628  ->params( $this->sprintfDate( $format, $ts->getTimestamp( TS_MW ) ) )
2629  ->text();
2630 
2631  // From here on in, the timestamp was soon enough ago so that we can simply say
2632  // XX units ago, e.g., "2 hours ago" or "5 minutes ago"
2633  } elseif ( $diff->h == 1 ) {
2634  // Less than 90 minutes, but more than an hour ago.
2635  $ts = wfMessage( 'hours-ago' )->inLanguage( $this )->numParams( 1 )->text();
2636  } elseif ( $diff->i >= 1 ) {
2637  // A few minutes ago.
2638  $ts = wfMessage( 'minutes-ago' )->inLanguage( $this )->numParams( $diff->i )->text();
2639  } elseif ( $diff->s >= 30 ) {
2640  // Less than a minute, but more than 30 sec ago.
2641  $ts = wfMessage( 'seconds-ago' )->inLanguage( $this )->numParams( $diff->s )->text();
2642  } else {
2643  // Less than 30 seconds ago.
2644  $ts = wfMessage( 'just-now' )->text();
2645  }
2646 
2647  return $ts;
2648  }
2649 
2654  public function getMessage( $key ) {
2655  return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
2656  }
2657 
2661  function getAllMessages() {
2662  return self::$dataCache->getItem( $this->mCode, 'messages' );
2663  }
2664 
2671  public function iconv( $in, $out, $string ) {
2672  # Even with //IGNORE iconv can whine about illegal characters in
2673  # *input* string. We just ignore those too.
2674  # REF: https://bugs.php.net/bug.php?id=37166
2675  # REF: https://phabricator.wikimedia.org/T18885
2676  Wikimedia\suppressWarnings();
2677  $text = iconv( $in, $out . '//IGNORE', $string );
2678  Wikimedia\restoreWarnings();
2679  return $text;
2680  }
2681 
2682  // callback functions for ucwords(), ucwordbreaks()
2683 
2689  return $this->ucfirst( $matches[1] );
2690  }
2691 
2697  return mb_strtoupper( $matches[0] );
2698  }
2699 
2705  return mb_strtoupper( $matches[0] );
2706  }
2707 
2715  public function ucfirst( $str ) {
2716  $o = ord( $str );
2717  if ( $o < 96 ) { // if already uppercase...
2718  return $str;
2719  } elseif ( $o < 128 ) {
2720  return ucfirst( $str ); // use PHP's ucfirst()
2721  } else {
2722  // fall back to more complex logic in case of multibyte strings
2723  return $this->uc( $str, true );
2724  }
2725  }
2726 
2735  public function uc( $str, $first = false ) {
2736  if ( $first ) {
2737  if ( $this->isMultibyte( $str ) ) {
2738  return mb_strtoupper( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2739  } else {
2740  return ucfirst( $str );
2741  }
2742  } else {
2743  return $this->isMultibyte( $str ) ? mb_strtoupper( $str ) : strtoupper( $str );
2744  }
2745  }
2746 
2751  function lcfirst( $str ) {
2752  $o = ord( $str );
2753  if ( !$o ) {
2754  return strval( $str );
2755  } elseif ( $o >= 128 ) {
2756  return $this->lc( $str, true );
2757  } elseif ( $o > 96 ) {
2758  return $str;
2759  } else {
2760  $str[0] = strtolower( $str[0] );
2761  return $str;
2762  }
2763  }
2764 
2770  function lc( $str, $first = false ) {
2771  if ( $first ) {
2772  if ( $this->isMultibyte( $str ) ) {
2773  return mb_strtolower( mb_substr( $str, 0, 1 ) ) . mb_substr( $str, 1 );
2774  } else {
2775  return strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 );
2776  }
2777  } else {
2778  return $this->isMultibyte( $str ) ? mb_strtolower( $str ) : strtolower( $str );
2779  }
2780  }
2781 
2786  function isMultibyte( $str ) {
2787  return strlen( $str ) !== mb_strlen( $str );
2788  }
2789 
2794  function ucwords( $str ) {
2795  if ( $this->isMultibyte( $str ) ) {
2796  $str = $this->lc( $str );
2797 
2798  // regexp to find first letter in each word (i.e. after each space)
2799  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)| ([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2800 
2801  // function to use to capitalize a single char
2802  return preg_replace_callback(
2803  $replaceRegexp,
2804  [ $this, 'ucwordsCallbackMB' ],
2805  $str
2806  );
2807  } else {
2808  return ucwords( strtolower( $str ) );
2809  }
2810  }
2811 
2818  function ucwordbreaks( $str ) {
2819  if ( $this->isMultibyte( $str ) ) {
2820  $str = $this->lc( $str );
2821 
2822  // since \b doesn't work for UTF-8, we explicitely define word break chars
2823  $breaks = "[ \-\(\)\}\{\.,\?!]";
2824 
2825  // find first letter after word break
2826  $replaceRegexp = "/^([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)|" .
2827  "$breaks([a-z]|[\\xc0-\\xff][\\x80-\\xbf]*)/";
2828 
2829  return preg_replace_callback(
2830  $replaceRegexp,
2831  [ $this, 'ucwordbreaksCallbackMB' ],
2832  $str
2833  );
2834  } else {
2835  return preg_replace_callback(
2836  '/\b([\w\x80-\xff]+)\b/',
2837  [ $this, 'ucwordbreaksCallbackAscii' ],
2838  $str
2839  );
2840  }
2841  }
2842 
2858  function caseFold( $s ) {
2859  return $this->uc( $s );
2860  }
2861 
2867  function checkTitleEncoding( $s ) {
2868  if ( is_array( $s ) ) {
2869  throw new MWException( 'Given array to checkTitleEncoding.' );
2870  }
2871  if ( StringUtils::isUtf8( $s ) ) {
2872  return $s;
2873  }
2874 
2875  return $this->iconv( $this->fallback8bitEncoding(), 'utf-8', $s );
2876  }
2877 
2882  return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
2883  }
2884 
2893  function hasWordBreaks() {
2894  return true;
2895  }
2896 
2904  function segmentByWord( $string ) {
2905  return $string;
2906  }
2907 
2915  function normalizeForSearch( $string ) {
2916  return self::convertDoubleWidth( $string );
2917  }
2918 
2927  protected static function convertDoubleWidth( $string ) {
2928  static $full = null;
2929  static $half = null;
2930 
2931  if ( $full === null ) {
2932  $fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2933  $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2934  $full = str_split( $fullWidth, 3 );
2935  $half = str_split( $halfWidth );
2936  }
2937 
2938  $string = str_replace( $full, $half, $string );
2939  return $string;
2940  }
2941 
2947  protected static function insertSpace( $string, $pattern ) {
2948  $string = preg_replace( $pattern, " $1 ", $string );
2949  $string = preg_replace( '/ +/', ' ', $string );
2950  return $string;
2951  }
2952 
2957  function convertForSearchResult( $termsArray ) {
2958  # some languages, e.g. Chinese, need to do a conversion
2959  # in order for search results to be displayed correctly
2960  return $termsArray;
2961  }
2962 
2969  function firstChar( $s ) {
2970  $matches = [];
2971  preg_match(
2972  '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' .
2973  '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})/',
2974  $s,
2975  $matches
2976  );
2977 
2978  if ( isset( $matches[1] ) ) {
2979  if ( strlen( $matches[1] ) != 3 ) {
2980  return $matches[1];
2981  }
2982 
2983  // Break down Hangul syllables to grab the first jamo
2984  $code = UtfNormal\Utils::utf8ToCodepoint( $matches[1] );
2985  if ( $code < 0xac00 || 0xd7a4 <= $code ) {
2986  return $matches[1];
2987  } elseif ( $code < 0xb098 ) {
2988  return "\u{3131}";
2989  } elseif ( $code < 0xb2e4 ) {
2990  return "\u{3134}";
2991  } elseif ( $code < 0xb77c ) {
2992  return "\u{3137}";
2993  } elseif ( $code < 0xb9c8 ) {
2994  return "\u{3139}";
2995  } elseif ( $code < 0xbc14 ) {
2996  return "\u{3141}";
2997  } elseif ( $code < 0xc0ac ) {
2998  return "\u{3142}";
2999  } elseif ( $code < 0xc544 ) {
3000  return "\u{3145}";
3001  } elseif ( $code < 0xc790 ) {
3002  return "\u{3147}";
3003  } elseif ( $code < 0xcc28 ) {
3004  return "\u{3148}";
3005  } elseif ( $code < 0xce74 ) {
3006  return "\u{314A}";
3007  } elseif ( $code < 0xd0c0 ) {
3008  return "\u{314B}";
3009  } elseif ( $code < 0xd30c ) {
3010  return "\u{314C}";
3011  } elseif ( $code < 0xd558 ) {
3012  return "\u{314D}";
3013  } else {
3014  return "\u{314E}";
3015  }
3016  } else {
3017  return '';
3018  }
3019  }
3020 
3024  function initEncoding() {
3025  wfDeprecated( __METHOD__, '1.28' );
3026  // No-op.
3027  }
3028 
3034  function recodeForEdit( $s ) {
3035  wfDeprecated( __METHOD__, '1.28' );
3036  return $s;
3037  }
3038 
3044  function recodeInput( $s ) {
3045  wfDeprecated( __METHOD__, '1.28' );
3046  return $s;
3047  }
3048 
3060  public function normalize( $s ) {
3061  global $wgAllUnicodeFixes;
3062  $s = UtfNormal\Validator::cleanUp( $s );
3063  if ( $wgAllUnicodeFixes ) {
3064  $s = $this->transformUsingPairFile( 'normalize-ar.php', $s );
3065  $s = $this->transformUsingPairFile( 'normalize-ml.php', $s );
3066  }
3067 
3068  return $s;
3069  }
3070 
3085  protected function transformUsingPairFile( $file, $string ) {
3086  if ( !isset( $this->transformData[$file] ) ) {
3087  global $IP;
3088  $data = require "$IP/languages/data/{$file}";
3089  $this->transformData[$file] = new ReplacementArray( $data );
3090  }
3091  return $this->transformData[$file]->replace( $string );
3092  }
3093 
3099  function isRTL() {
3100  return self::$dataCache->getItem( $this->mCode, 'rtl' );
3101  }
3102 
3107  function getDir() {
3108  return $this->isRTL() ? 'rtl' : 'ltr';
3109  }
3110 
3119  function alignStart() {
3120  return $this->isRTL() ? 'right' : 'left';
3121  }
3122 
3131  function alignEnd() {
3132  return $this->isRTL() ? 'left' : 'right';
3133  }
3134 
3146  function getDirMarkEntity( $opposite = false ) {
3147  if ( $opposite ) {
3148  return $this->isRTL() ? '&lrm;' : '&rlm;';
3149  }
3150  return $this->isRTL() ? '&rlm;' : '&lrm;';
3151  }
3152 
3163  function getDirMark( $opposite = false ) {
3164  $lrm = "\u{200E}"; # LEFT-TO-RIGHT MARK, commonly abbreviated LRM
3165  $rlm = "\u{200F}"; # RIGHT-TO-LEFT MARK, commonly abbreviated RLM
3166  if ( $opposite ) {
3167  return $this->isRTL() ? $lrm : $rlm;
3168  }
3169  return $this->isRTL() ? $rlm : $lrm;
3170  }
3171 
3175  function capitalizeAllNouns() {
3176  return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
3177  }
3178 
3186  function getArrow( $direction = 'forwards' ) {
3187  switch ( $direction ) {
3188  case 'forwards':
3189  return $this->isRTL() ? '←' : '→';
3190  case 'backwards':
3191  return $this->isRTL() ? '→' : '←';
3192  case 'left':
3193  return '←';
3194  case 'right':
3195  return '→';
3196  case 'up':
3197  return '↑';
3198  case 'down':
3199  return '↓';
3200  }
3201  }
3202 
3208  function linkPrefixExtension() {
3209  return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
3210  }
3211 
3216  function getMagicWords() {
3217  return self::$dataCache->getItem( $this->mCode, 'magicWords' );
3218  }
3219 
3223  protected function doMagicHook() {
3224  if ( $this->mMagicHookDone ) {
3225  return;
3226  }
3227  $this->mMagicHookDone = true;
3228  Hooks::run( 'LanguageGetMagic', [ &$this->mMagicExtensions, $this->getCode() ], '1.16' );
3229  }
3230 
3236  function getMagic( $mw ) {
3237  // Saves a function call
3238  if ( !$this->mMagicHookDone ) {
3239  $this->doMagicHook();
3240  }
3241 
3242  if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
3243  $rawEntry = $this->mMagicExtensions[$mw->mId];
3244  } else {
3245  $rawEntry = self::$dataCache->getSubitem(
3246  $this->mCode, 'magicWords', $mw->mId );
3247  }
3248 
3249  if ( !is_array( $rawEntry ) ) {
3250  wfWarn( "\"$rawEntry\" is not a valid magic word for \"$mw->mId\"" );
3251  } else {
3252  $mw->mCaseSensitive = $rawEntry[0];
3253  $mw->mSynonyms = array_slice( $rawEntry, 1 );
3254  }
3255  }
3256 
3262  function addMagicWordsByLang( $newWords ) {
3263  $fallbackChain = $this->getFallbackLanguages();
3264  $fallbackChain = array_reverse( $fallbackChain );
3265  foreach ( $fallbackChain as $code ) {
3266  if ( isset( $newWords[$code] ) ) {
3267  $this->mMagicExtensions = $newWords[$code] + $this->mMagicExtensions;
3268  }
3269  }
3270  }
3271 
3278  // Cache aliases because it may be slow to load them
3279  if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
3280  // Initialise array
3281  $this->mExtendedSpecialPageAliases =
3282  self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
3283  Hooks::run( 'LanguageGetSpecialPageAliases',
3284  [ &$this->mExtendedSpecialPageAliases, $this->getCode() ], '1.16' );
3285  }
3286 
3288  }
3289 
3296  function emphasize( $text ) {
3297  return "<em>$text</em>";
3298  }
3299 
3322  public function formatNum( $number, $nocommafy = false ) {
3323  global $wgTranslateNumerals;
3324  if ( !$nocommafy ) {
3325  $number = $this->commafy( $number );
3326  $s = $this->separatorTransformTable();
3327  if ( $s ) {
3328  $number = strtr( $number, $s );
3329  }
3330  }
3331 
3332  if ( $wgTranslateNumerals ) {
3333  $s = $this->digitTransformTable();
3334  if ( $s ) {
3335  $number = strtr( $number, $s );
3336  }
3337  }
3338 
3339  return (string)$number;
3340  }
3341 
3350  public function formatNumNoSeparators( $number ) {
3351  return $this->formatNum( $number, true );
3352  }
3353 
3358  public function parseFormattedNumber( $number ) {
3359  $s = $this->digitTransformTable();
3360  if ( $s ) {
3361  // eliminate empty array values such as ''. (T66347)
3362  $s = array_filter( $s );
3363  $number = strtr( $number, array_flip( $s ) );
3364  }
3365 
3366  $s = $this->separatorTransformTable();
3367  if ( $s ) {
3368  // eliminate empty array values such as ''. (T66347)
3369  $s = array_filter( $s );
3370  $number = strtr( $number, array_flip( $s ) );
3371  }
3372 
3373  $number = strtr( $number, [ ',' => '' ] );
3374  return $number;
3375  }
3376 
3383  function commafy( $number ) {
3386  if ( $number === null ) {
3387  return '';
3388  }
3389 
3390  if ( !$digitGroupingPattern || $digitGroupingPattern === "###,###,###" ) {
3391  // Default grouping is at thousands, use the same for ###,###,### pattern too.
3392  // In some languages it's conventional not to insert a thousands separator
3393  // in numbers that are four digits long (1000-9999).
3394  if ( $minimumGroupingDigits ) {
3395  // Number of '#' characters after last comma in the grouping pattern.
3396  // The pattern is hardcoded here, but this would vary for different patterns.
3397  $primaryGroupingSize = 3;
3398  // Maximum length of a number to suppress digit grouping for.
3399  $maximumLength = $minimumGroupingDigits + $primaryGroupingSize - 1;
3400  if ( preg_match( '/^\-?\d{1,' . $maximumLength . '}(\.\d+)?$/', $number ) ) {
3401  return $number;
3402  }
3403  }
3404  return strrev( (string)preg_replace( '/(\d{3})(?=\d)(?!\d*\.)/', '$1,', strrev( $number ) ) );
3405  } else {
3406  // Ref: http://cldr.unicode.org/translation/number-patterns
3407  $sign = "";
3408  if ( intval( $number ) < 0 ) {
3409  // For negative numbers apply the algorithm like positive number and add sign.
3410  $sign = "-";
3411  $number = substr( $number, 1 );
3412  }
3413  $integerPart = [];
3414  $decimalPart = [];
3415  $numMatches = preg_match_all( "/(#+)/", $digitGroupingPattern, $matches );
3416  preg_match( "/\d+/", $number, $integerPart );
3417  preg_match( "/\.\d*/", $number, $decimalPart );
3418  $groupedNumber = ( count( $decimalPart ) > 0 ) ? $decimalPart[0] : "";
3419  if ( $groupedNumber === $number ) {
3420  // the string does not have any number part. Eg: .12345
3421  return $sign . $groupedNumber;
3422  }
3423  $start = $end = ( $integerPart ) ? strlen( $integerPart[0] ) : 0;
3424  while ( $start > 0 ) {
3425  $match = $matches[0][$numMatches - 1];
3426  $matchLen = strlen( $match );
3427  $start = $end - $matchLen;
3428  if ( $start < 0 ) {
3429  $start = 0;
3430  }
3431  $groupedNumber = substr( $number, $start, $end - $start ) . $groupedNumber;
3432  $end = $start;
3433  if ( $numMatches > 1 ) {
3434  // use the last pattern for the rest of the number
3435  $numMatches--;
3436  }
3437  if ( $start > 0 ) {
3438  $groupedNumber = "," . $groupedNumber;
3439  }
3440  }
3441  return $sign . $groupedNumber;
3442  }
3443  }
3444 
3449  return self::$dataCache->getItem( $this->mCode, 'digitGroupingPattern' );
3450  }
3451 
3455  function digitTransformTable() {
3456  return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
3457  }
3458 
3463  return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
3464  }
3465 
3470  return self::$dataCache->getItem( $this->mCode, 'minimumGroupingDigits' );
3471  }
3472 
3481  public function listToText( array $list ) {
3482  $itemCount = count( $list );
3483  if ( $itemCount < 1 ) {
3484  return '';
3485  }
3486  $text = array_pop( $list );
3487  if ( $itemCount > 1 ) {
3488  $and = $this->msg( 'and' )->escaped();
3489  $space = $this->msg( 'word-separator' )->escaped();
3490  $comma = '';
3491  if ( $itemCount > 2 ) {
3492  $comma = $this->msg( 'comma-separator' )->escaped();
3493  }
3494  $text = implode( $comma, $list ) . $and . $space . $text;
3495  }
3496  return $text;
3497  }
3498 
3505  function commaList( array $list ) {
3506  return implode(
3507  wfMessage( 'comma-separator' )->inLanguage( $this )->escaped(),
3508  $list
3509  );
3510  }
3511 
3518  function semicolonList( array $list ) {
3519  return implode(
3520  wfMessage( 'semicolon-separator' )->inLanguage( $this )->escaped(),
3521  $list
3522  );
3523  }
3524 
3530  function pipeList( array $list ) {
3531  return implode(
3532  wfMessage( 'pipe-separator' )->inLanguage( $this )->escaped(),
3533  $list
3534  );
3535  }
3536 
3554  function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3555  wfDeprecated( __METHOD__, '1.31' );
3556  return $this->truncateForDatabase( $string, $length, $ellipsis, $adjustLength );
3557  }
3558 
3574  function truncateForDatabase( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3575  return $this->truncateInternal(
3576  $string, $length, $ellipsis, $adjustLength, 'strlen', 'substr'
3577  );
3578  }
3579 
3598  function truncateForVisual( $string, $length, $ellipsis = '...', $adjustLength = true ) {
3599  // Passing encoding to mb_strlen and mb_substr is optional.
3600  // Encoding defaults to mb_internal_encoding(), which is set to UTF-8 in Setup.php, so
3601  // explicit specification of encoding is skipped.
3602  // Note: Both multibyte methods are callables invoked in truncateInternal.
3603  return $this->truncateInternal(
3604  $string, $length, $ellipsis, $adjustLength, 'mb_strlen', 'mb_substr'
3605  );
3606  }
3607 
3624  private function truncateInternal(
3625  $string, $length, $ellipsis, $adjustLength, $measureLength, $getSubstring
3626  ) {
3627  if ( !is_callable( $measureLength ) || !is_callable( $getSubstring ) ) {
3628  throw new InvalidArgumentException( 'Invalid callback provided' );
3629  }
3630 
3631  # Check if there is no need to truncate
3632  if ( $measureLength( $string ) <= abs( $length ) ) {
3633  return $string; // no need to truncate
3634  }
3635 
3636  # Use the localized ellipsis character
3637  if ( $ellipsis == '...' ) {
3638  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3639  }
3640  if ( $length == 0 ) {
3641  return $ellipsis; // convention
3642  }
3643 
3644  $stringOriginal = $string;
3645  # If ellipsis length is >= $length then we can't apply $adjustLength
3646  if ( $adjustLength && $measureLength( $ellipsis ) >= abs( $length ) ) {
3647  $string = $ellipsis; // this can be slightly unexpected
3648  # Otherwise, truncate and add ellipsis...
3649  } else {
3650  $ellipsisLength = $adjustLength ? $measureLength( $ellipsis ) : 0;
3651  if ( $length > 0 ) {
3652  $length -= $ellipsisLength;
3653  $string = $getSubstring( $string, 0, $length ); // xyz...
3654  $string = $this->removeBadCharLast( $string );
3655  $string = rtrim( $string );
3656  $string = $string . $ellipsis;
3657  } else {
3658  $length += $ellipsisLength;
3659  $string = $getSubstring( $string, $length ); // ...xyz
3660  $string = $this->removeBadCharFirst( $string );
3661  $string = ltrim( $string );
3662  $string = $ellipsis . $string;
3663  }
3664  }
3665 
3666  # Do not truncate if the ellipsis makes the string longer/equal (T24181).
3667  # This check is *not* redundant if $adjustLength, due to the single case where
3668  # LEN($ellipsis) > ABS($limit arg); $stringOriginal could be shorter than $string.
3669  if ( $measureLength( $string ) < $measureLength( $stringOriginal ) ) {
3670  return $string;
3671  } else {
3672  return $stringOriginal;
3673  }
3674  }
3675 
3683  protected function removeBadCharLast( $string ) {
3684  if ( $string != '' ) {
3685  $char = ord( $string[strlen( $string ) - 1] );
3686  $m = [];
3687  if ( $char >= 0xc0 ) {
3688  # We got the first byte only of a multibyte char; remove it.
3689  $string = substr( $string, 0, -1 );
3690  } elseif ( $char >= 0x80 &&
3691  // Use the /s modifier (PCRE_DOTALL) so (.*) also matches newlines
3692  preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
3693  '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
3694  ) {
3695  # We chopped in the middle of a character; remove it
3696  $string = $m[1];
3697  }
3698  }
3699  return $string;
3700  }
3701 
3709  protected function removeBadCharFirst( $string ) {
3710  if ( $string != '' ) {
3711  $char = ord( $string[0] );
3712  if ( $char >= 0x80 && $char < 0xc0 ) {
3713  # We chopped in the middle of a character; remove the whole thing
3714  $string = preg_replace( '/^[\x80-\xbf]+/', '', $string );
3715  }
3716  }
3717  return $string;
3718  }
3719 
3735  function truncateHtml( $text, $length, $ellipsis = '...' ) {
3736  # Use the localized ellipsis character
3737  if ( $ellipsis == '...' ) {
3738  $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this )->escaped();
3739  }
3740  # Check if there is clearly no need to truncate
3741  if ( $length <= 0 ) {
3742  return $ellipsis; // no text shown, nothing to format (convention)
3743  } elseif ( strlen( $text ) <= $length ) {
3744  return $text; // string short enough even *with* HTML (short-circuit)
3745  }
3746 
3747  $dispLen = 0; // innerHTML legth so far
3748  $testingEllipsis = false; // checking if ellipses will make string longer/equal?
3749  $tagType = 0; // 0-open, 1-close
3750  $bracketState = 0; // 1-tag start, 2-tag name, 0-neither
3751  $entityState = 0; // 0-not entity, 1-entity
3752  $tag = $ret = ''; // accumulated tag name, accumulated result string
3753  $openTags = []; // open tag stack
3754  $maybeState = null; // possible truncation state
3755 
3756  $textLen = strlen( $text );
3757  $neLength = max( 0, $length - strlen( $ellipsis ) ); // non-ellipsis len if truncated
3758  for ( $pos = 0; true; ++$pos ) {
3759  # Consider truncation once the display length has reached the maximim.
3760  # We check if $dispLen > 0 to grab tags for the $neLength = 0 case.
3761  # Check that we're not in the middle of a bracket/entity...
3762  if ( $dispLen && $dispLen >= $neLength && $bracketState == 0 && !$entityState ) {
3763  if ( !$testingEllipsis ) {
3764  $testingEllipsis = true;
3765  # Save where we are; we will truncate here unless there turn out to
3766  # be so few remaining characters that truncation is not necessary.
3767  if ( !$maybeState ) { // already saved? ($neLength = 0 case)
3768  $maybeState = [ $ret, $openTags ]; // save state
3769  }
3770  } elseif ( $dispLen > $length && $dispLen > strlen( $ellipsis ) ) {
3771  # String in fact does need truncation, the truncation point was OK.
3772  list( $ret, $openTags ) = $maybeState; // reload state
3773  $ret = $this->removeBadCharLast( $ret ); // multi-byte char fix
3774  $ret .= $ellipsis; // add ellipsis
3775  break;
3776  }
3777  }
3778  if ( $pos >= $textLen ) {
3779  break; // extra iteration just for above checks
3780  }
3781 
3782  # Read the next char...
3783  $ch = $text[$pos];
3784  $lastCh = $pos ? $text[$pos - 1] : '';
3785  $ret .= $ch; // add to result string
3786  if ( $ch == '<' ) {
3787  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags ); // for bad HTML
3788  $entityState = 0; // for bad HTML
3789  $bracketState = 1; // tag started (checking for backslash)
3790  } elseif ( $ch == '>' ) {
3791  $this->truncate_endBracket( $tag, $tagType, $lastCh, $openTags );
3792  $entityState = 0; // for bad HTML
3793  $bracketState = 0; // out of brackets
3794  } elseif ( $bracketState == 1 ) {
3795  if ( $ch == '/' ) {
3796  $tagType = 1; // close tag (e.g. "</span>")
3797  } else {
3798  $tagType = 0; // open tag (e.g. "<span>")
3799  $tag .= $ch;
3800  }
3801  $bracketState = 2; // building tag name
3802  } elseif ( $bracketState == 2 ) {
3803  if ( $ch != ' ' ) {
3804  $tag .= $ch;
3805  } else {
3806  // Name found (e.g. "<a href=..."), add on tag attributes...
3807  $pos += $this->truncate_skip( $ret, $text, "<>", $pos + 1 );
3808  }
3809  } elseif ( $bracketState == 0 ) {
3810  if ( $entityState ) {
3811  if ( $ch == ';' ) {
3812  $entityState = 0;
3813  $dispLen++; // entity is one displayed char
3814  }
3815  } else {
3816  if ( $neLength == 0 && !$maybeState ) {
3817  // Save state without $ch. We want to *hit* the first
3818  // display char (to get tags) but not *use* it if truncating.
3819  $maybeState = [ substr( $ret, 0, -1 ), $openTags ];
3820  }
3821  if ( $ch == '&' ) {
3822  $entityState = 1; // entity found, (e.g. "&#160;")
3823  } else {
3824  $dispLen++; // this char is displayed
3825  // Add the next $max display text chars after this in one swoop...
3826  $max = ( $testingEllipsis ? $length : $neLength ) - $dispLen;
3827  $skipped = $this->truncate_skip( $ret, $text, "<>&", $pos + 1, $max );
3828  $dispLen += $skipped;
3829  $pos += $skipped;
3830  }
3831  }
3832  }
3833  }
3834  // Close the last tag if left unclosed by bad HTML
3835  $this->truncate_endBracket( $tag, $text[$textLen - 1], $tagType, $openTags );
3836  while ( count( $openTags ) > 0 ) {
3837  $ret .= '</' . array_pop( $openTags ) . '>'; // close open tags
3838  }
3839  return $ret;
3840  }
3841 
3853  private function truncate_skip( &$ret, $text, $search, $start, $len = null ) {
3854  if ( $len === null ) {
3855  $len = -1; // -1 means "no limit" for strcspn
3856  } elseif ( $len < 0 ) {
3857  $len = 0; // sanity
3858  }
3859  $skipCount = 0;
3860  if ( $start < strlen( $text ) ) {
3861  $skipCount = strcspn( $text, $search, $start, $len );
3862  $ret .= substr( $text, $start, $skipCount );
3863  }
3864  return $skipCount;
3865  }
3866 
3876  private function truncate_endBracket( &$tag, $tagType, $lastCh, &$openTags ) {
3877  $tag = ltrim( $tag );
3878  if ( $tag != '' ) {
3879  if ( $tagType == 0 && $lastCh != '/' ) {
3880  $openTags[] = $tag; // tag opened (didn't close itself)
3881  } elseif ( $tagType == 1 ) {
3882  if ( $openTags && $tag == $openTags[count( $openTags ) - 1] ) {
3883  array_pop( $openTags ); // tag closed
3884  }
3885  }
3886  $tag = '';
3887  }
3888  }
3889 
3898  function convertGrammar( $word, $case ) {
3899  global $wgGrammarForms;
3900  if ( isset( $wgGrammarForms[$this->getCode()][$case][$word] ) ) {
3901  return $wgGrammarForms[$this->getCode()][$case][$word];
3902  }
3903 
3905 
3906  if ( isset( $grammarTransformations[$case] ) ) {
3907  $forms = $grammarTransformations[$case];
3908 
3909  // Some names of grammar rules are aliases for other rules.
3910  // In such cases the value is a string rather than object,
3911  // so load the actual rules.
3912  if ( is_string( $forms ) ) {
3913  $forms = $grammarTransformations[$forms];
3914  }
3915 
3916  foreach ( array_values( $forms ) as $rule ) {
3917  $form = $rule[0];
3918 
3919  if ( $form === '@metadata' ) {
3920  continue;
3921  }
3922 
3923  $replacement = $rule[1];
3924 
3925  $regex = '/' . addcslashes( $form, '/' ) . '/u';
3926  $patternMatches = preg_match( $regex, $word );
3927 
3928  if ( $patternMatches === false ) {
3929  wfLogWarning(
3930  'An error occurred while processing grammar. ' .
3931  "Word: '$word'. Regex: /$form/."
3932  );
3933  } elseif ( $patternMatches === 1 ) {
3934  $word = preg_replace( $regex, $replacement, $word );
3935 
3936  break;
3937  }
3938  }
3939  }
3940 
3941  return $word;
3942  }
3943 
3949  function getGrammarForms() {
3950  global $wgGrammarForms;
3951  if ( isset( $wgGrammarForms[$this->getCode()] )
3952  && is_array( $wgGrammarForms[$this->getCode()] )
3953  ) {
3954  return $wgGrammarForms[$this->getCode()];
3955  }
3956 
3957  return [];
3958  }
3959 
3969  public function getGrammarTransformations() {
3970  $languageCode = $this->getCode();
3971 
3972  if ( self::$grammarTransformations === null ) {
3973  self::$grammarTransformations = new MapCacheLRU( 10 );
3974  }
3975 
3976  if ( self::$grammarTransformations->has( $languageCode ) ) {
3977  return self::$grammarTransformations->get( $languageCode );
3978  }
3979 
3980  $data = [];
3981 
3982  $grammarDataFile = __DIR__ . "/data/grammarTransformations/$languageCode.json";
3983  if ( is_readable( $grammarDataFile ) ) {
3984  $data = FormatJson::decode(
3985  file_get_contents( $grammarDataFile ),
3986  true
3987  );
3988 
3989  if ( $data === null ) {
3990  throw new MWException( "Invalid grammar data for \"$languageCode\"." );
3991  }
3992 
3993  self::$grammarTransformations->set( $languageCode, $data );
3994  }
3995 
3996  return $data;
3997  }
3998 
4018  function gender( $gender, $forms ) {
4019  if ( !count( $forms ) ) {
4020  return '';
4021  }
4022  $forms = $this->preConvertPlural( $forms, 2 );
4023  if ( $gender === 'male' ) {
4024  return $forms[0];
4025  }
4026  if ( $gender === 'female' ) {
4027  return $forms[1];
4028  }
4029  return $forms[2] ?? $forms[0];
4030  }
4031 
4047  function convertPlural( $count, $forms ) {
4048  // Handle explicit n=pluralform cases
4049  $forms = $this->handleExplicitPluralForms( $count, $forms );
4050  if ( is_string( $forms ) ) {
4051  return $forms;
4052  }
4053  if ( !count( $forms ) ) {
4054  return '';
4055  }
4056 
4057  $pluralForm = $this->getPluralRuleIndexNumber( $count );
4058  $pluralForm = min( $pluralForm, count( $forms ) - 1 );
4059  return $forms[$pluralForm];
4060  }
4061 
4077  protected function handleExplicitPluralForms( $count, array $forms ) {
4078  foreach ( $forms as $index => $form ) {
4079  if ( preg_match( '/\d+=/i', $form ) ) {
4080  $pos = strpos( $form, '=' );
4081  if ( substr( $form, 0, $pos ) === (string)$count ) {
4082  return substr( $form, $pos + 1 );
4083  }
4084  unset( $forms[$index] );
4085  }
4086  }
4087  return array_values( $forms );
4088  }
4089 
4098  protected function preConvertPlural( /* Array */ $forms, $count ) {
4099  while ( count( $forms ) < $count ) {
4100  $forms[] = $forms[count( $forms ) - 1];
4101  }
4102  return $forms;
4103  }
4104 
4121  public function embedBidi( $text = '' ) {
4122  $dir = self::strongDirFromContent( $text );
4123  if ( $dir === 'ltr' ) {
4124  // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING
4125  return self::$lre . $text . self::$pdf;
4126  }
4127  if ( $dir === 'rtl' ) {
4128  // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING
4129  return self::$rle . $text . self::$pdf;
4130  }
4131  // No strong directionality: do not wrap
4132  return $text;
4133  }
4134 
4148  function translateBlockExpiry( $str, User $user = null, $now = 0 ) {
4149  $duration = SpecialBlock::getSuggestedDurations( $this );
4150  foreach ( $duration as $show => $value ) {
4151  if ( strcmp( $str, $value ) == 0 ) {
4152  return htmlspecialchars( trim( $show ) );
4153  }
4154  }
4155 
4156  if ( wfIsInfinity( $str ) ) {
4157  foreach ( $duration as $show => $value ) {
4158  if ( wfIsInfinity( $value ) ) {
4159  return htmlspecialchars( trim( $show ) );
4160  }
4161  }
4162  }
4163 
4164  // If all else fails, return a standard duration or timestamp description.
4165  $time = strtotime( $str, $now );
4166  if ( $time === false ) { // Unknown format. Return it as-is in case.
4167  return $str;
4168  } elseif ( $time !== strtotime( $str, $now + 1 ) ) { // It's a relative timestamp.
4169  // The result differs based on current time, so the difference
4170  // is a fixed duration length.
4171  return $this->formatDuration( $time - $now );
4172  } else { // It's an absolute timestamp.
4173  if ( $time === 0 ) {
4174  // wfTimestamp() handles 0 as current time instead of epoch.
4175  $time = '19700101000000';
4176  }
4177  if ( $user ) {
4178  return $this->userTimeAndDate( $time, $user );
4179  }
4180  return $this->timeanddate( $time );
4181  }
4182  }
4183 
4191  public function segmentForDiff( $text ) {
4192  return $text;
4193  }
4194 
4201  public function unsegmentForDiff( $text ) {
4202  return $text;
4203  }
4204 
4211  public function getConverter() {
4212  return $this->mConverter;
4213  }
4214 
4223  public function autoConvert( $text, $variant = false ) {
4224  return $this->mConverter->autoConvert( $text, $variant );
4225  }
4226 
4233  public function autoConvertToAllVariants( $text ) {
4234  return $this->mConverter->autoConvertToAllVariants( $text );
4235  }
4236 
4248  public function convert( $text ) {
4249  return $this->mConverter->convert( $text );
4250  }
4251 
4258  public function convertTitle( $title ) {
4259  return $this->mConverter->convertTitle( $title );
4260  }
4261 
4270  public function convertNamespace( $ns, $variant = null ) {
4271  return $this->mConverter->convertNamespace( $ns, $variant );
4272  }
4273 
4279  public function hasVariants() {
4280  return count( $this->getVariants() ) > 1;
4281  }
4282 
4293  public function hasVariant( $variant ) {
4294  return $variant && ( $variant === $this->mConverter->validateVariant( $variant ) );
4295  }
4296 
4304  public function convertHtml( $text, $isTitle = false ) {
4305  return htmlspecialchars( $this->convert( $text, $isTitle ) );
4306  }
4307 
4312  public function convertCategoryKey( $key ) {
4313  return $this->mConverter->convertCategoryKey( $key );
4314  }
4315 
4322  public function getVariants() {
4323  return $this->mConverter->getVariants();
4324  }
4325 
4329  public function getPreferredVariant() {
4330  return $this->mConverter->getPreferredVariant();
4331  }
4332 
4336  public function getDefaultVariant() {
4337  return $this->mConverter->getDefaultVariant();
4338  }
4339 
4343  public function getURLVariant() {
4344  return $this->mConverter->getURLVariant();
4345  }
4346 
4359  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
4360  $this->mConverter->findVariantLink( $link, $nt, $ignoreOtherCond );
4361  }
4362 
4369  function getExtraHashOptions() {
4370  return $this->mConverter->getExtraHashOptions();
4371  }
4372 
4380  public function getParsedTitle() {
4381  return $this->mConverter->getParsedTitle();
4382  }
4383 
4390  public function updateConversionTable( Title $title ) {
4391  $this->mConverter->updateConversionTable( $title );
4392  }
4393 
4410  public function markNoConversion( $text, $noParse = false ) {
4411  wfDeprecated( __METHOD__, '1.32' );
4412  // Excluding protocal-relative URLs may avoid many false positives.
4413  if ( $noParse || preg_match( '/^(?:' . wfUrlProtocolsWithoutProtRel() . ')/', $text ) ) {
4414  return $this->mConverter->markNoConversion( $text );
4415  } else {
4416  return $text;
4417  }
4418  }
4419 
4426  public function linkTrail() {
4427  return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
4428  }
4429 
4436  public function linkPrefixCharset() {
4437  return self::$dataCache->getItem( $this->mCode, 'linkPrefixCharset' );
4438  }
4439 
4447  public function getParentLanguage() {
4448  if ( $this->mParentLanguage !== false ) {
4449  return $this->mParentLanguage;
4450  }
4451 
4452  $code = explode( '-', $this->getCode() )[0];
4453  if ( !in_array( $code, LanguageConverter::$languagesWithVariants ) ) {
4454  $this->mParentLanguage = null;
4455  return null;
4456  }
4457  $lang = self::factory( $code );
4458  if ( !$lang->hasVariant( $this->getCode() ) ) {
4459  $this->mParentLanguage = null;
4460  return null;
4461  }
4462 
4463  $this->mParentLanguage = $lang;
4464  return $lang;
4465  }
4466 
4474  public function equals( Language $lang ) {
4475  return $lang === $this || $lang->getCode() === $this->mCode;
4476  }
4477 
4486  public function getCode() {
4487  return $this->mCode;
4488  }
4489 
4500  public function getHtmlCode() {
4501  if ( is_null( $this->mHtmlCode ) ) {
4502  $this->mHtmlCode = LanguageCode::bcp47( $this->getCode() );
4503  }
4504  return $this->mHtmlCode;
4505  }
4506 
4511  public function setCode( $code ) {
4512  $this->mCode = $code;
4513  // Ensure we don't leave incorrect cached data lying around
4514  $this->mHtmlCode = null;
4515  $this->mParentLanguage = false;
4516  }
4517 
4525  public static function getCodeFromFileName( $filename, $prefix = 'Language', $suffix = '.php' ) {
4526  $m = null;
4527  preg_match( '/' . preg_quote( $prefix, '/' ) . '([A-Z][a-z_]+)' .
4528  preg_quote( $suffix, '/' ) . '/', $filename, $m );
4529  if ( !count( $m ) ) {
4530  return false;
4531  }
4532  return str_replace( '_', '-', strtolower( $m[1] ) );
4533  }
4534 
4540  public static function classFromCode( $code, $fallback = true ) {
4541  if ( $fallback && $code == 'en' ) {
4542  return 'Language';
4543  } else {
4544  return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
4545  }
4546  }
4547 
4556  public static function getFileName( $prefix, $code, $suffix = '.php' ) {
4557  if ( !self::isValidBuiltInCode( $code ) ) {
4558  throw new MWException( "Invalid language code \"$code\"" );
4559  }
4560 
4561  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
4562  }
4563 
4568  public static function getMessagesFileName( $code ) {
4569  global $IP;
4570  $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
4571  Hooks::run( 'Language::getMessagesFileName', [ $code, &$file ] );
4572  return $file;
4573  }
4574 
4581  public static function getJsonMessagesFileName( $code ) {
4582  global $IP;
4583 
4584  if ( !self::isValidBuiltInCode( $code ) ) {
4585  throw new MWException( "Invalid language code \"$code\"" );
4586  }
4587 
4588  return "$IP/languages/i18n/$code.json";
4589  }
4590 
4598  public static function getFallbackFor( $code ) {
4599  $fallbacks = self::getFallbacksFor( $code );
4600  if ( $fallbacks ) {
4601  return $fallbacks[0];
4602  }
4603  return false;
4604  }
4605 
4616  public static function getFallbacksFor( $code, $mode = self::MESSAGES_FALLBACKS ) {
4617  if ( $code === 'en' || !self::isValidBuiltInCode( $code ) ) {
4618  return [];
4619  }
4620  switch ( $mode ) {
4622  // For unknown languages, fallbackSequence returns an empty array,
4623  // hardcode fallback to 'en' in that case as English messages are
4624  // always defined.
4625  return self::getLocalisationCache()->getItem( $code, 'fallbackSequence' ) ?: [ 'en' ];
4627  // Use this mode when you don't want to fallback to English unless
4628  // explicitly defined, for example when you have language-variant icons
4629  // and an international language-independent fallback.
4630  return self::getLocalisationCache()->getItem( $code, 'originalFallbackSequence' );
4631  default:
4632  throw new MWException( "Invalid fallback mode \"$mode\"" );
4633  }
4634  }
4635 
4644  public static function getFallbacksIncludingSiteLanguage( $code ) {
4645  global $wgLanguageCode;
4646 
4647  // Usually, we will only store a tiny number of fallback chains, so we
4648  // keep them in static memory.
4649  $cacheKey = "{$code}-{$wgLanguageCode}";
4650 
4651  if ( !array_key_exists( $cacheKey, self::$fallbackLanguageCache ) ) {
4652  $fallbacks = self::getFallbacksFor( $code );
4653 
4654  // Append the site's fallback chain, including the site language itself
4655  $siteFallbacks = self::getFallbacksFor( $wgLanguageCode );
4656  array_unshift( $siteFallbacks, $wgLanguageCode );
4657 
4658  // Eliminate any languages already included in the chain
4659  $siteFallbacks = array_diff( $siteFallbacks, $fallbacks );
4660 
4661  self::$fallbackLanguageCache[$cacheKey] = [ $fallbacks, $siteFallbacks ];
4662  }
4663  return self::$fallbackLanguageCache[$cacheKey];
4664  }
4665 
4675  public static function getMessagesFor( $code ) {
4676  return self::getLocalisationCache()->getItem( $code, 'messages' );
4677  }
4678 
4687  public static function getMessageFor( $key, $code ) {
4688  return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
4689  }
4690 
4699  public static function getMessageKeysFor( $code ) {
4700  return self::getLocalisationCache()->getSubitemList( $code, 'messages' );
4701  }
4702 
4707  function fixVariableInNamespace( $talk ) {
4708  if ( strpos( $talk, '$1' ) === false ) {
4709  return $talk;
4710  }
4711 
4712  global $wgMetaNamespace;
4713  $talk = str_replace( '$1', $wgMetaNamespace, $talk );
4714 
4715  # Allow grammar transformations
4716  # Allowing full message-style parsing would make simple requests
4717  # such as action=raw much more expensive than they need to be.
4718  # This will hopefully cover most cases.
4719  $talk = preg_replace_callback( '/{{grammar:(.*?)\|(.*?)}}/i',
4720  [ $this, 'replaceGrammarInNamespace' ], $talk );
4721  return str_replace( ' ', '_', $talk );
4722  }
4723 
4728  function replaceGrammarInNamespace( $m ) {
4729  return $this->convertGrammar( trim( $m[2] ), trim( $m[1] ) );
4730  }
4731 
4742  public function formatExpiry( $expiry, $format = true, $infinity = 'infinity' ) {
4743  static $dbInfinity;
4744  if ( $dbInfinity === null ) {
4745  $dbInfinity = wfGetDB( DB_REPLICA )->getInfinity();
4746  }
4747 
4748  if ( $expiry == '' || $expiry === 'infinity' || $expiry == $dbInfinity ) {
4749  return $format === true
4750  ? $this->getMessageFromDB( 'infiniteblock' )
4751  : $infinity;
4752  } else {
4753  return $format === true
4754  ? $this->timeanddate( $expiry, /* User preference timezone */ true )
4755  : wfTimestamp( $format, $expiry );
4756  }
4757  }
4758 
4772  function formatTimePeriod( $seconds, $format = [] ) {
4773  if ( !is_array( $format ) ) {
4774  $format = [ 'avoid' => $format ]; // For backwards compatibility
4775  }
4776  if ( !isset( $format['avoid'] ) ) {
4777  $format['avoid'] = false;
4778  }
4779  if ( !isset( $format['noabbrevs'] ) ) {
4780  $format['noabbrevs'] = false;
4781  }
4782  $secondsMsg = wfMessage(
4783  $format['noabbrevs'] ? 'seconds' : 'seconds-abbrev' )->inLanguage( $this );
4784  $minutesMsg = wfMessage(
4785  $format['noabbrevs'] ? 'minutes' : 'minutes-abbrev' )->inLanguage( $this );
4786  $hoursMsg = wfMessage(
4787  $format['noabbrevs'] ? 'hours' : 'hours-abbrev' )->inLanguage( $this );
4788  $daysMsg = wfMessage(
4789  $format['noabbrevs'] ? 'days' : 'days-abbrev' )->inLanguage( $this );
4790 
4791  if ( round( $seconds * 10 ) < 100 ) {
4792  $s = $this->formatNum( sprintf( "%.1f", round( $seconds * 10 ) / 10 ) );
4793  $s = $secondsMsg->params( $s )->text();
4794  } elseif ( round( $seconds ) < 60 ) {
4795  $s = $this->formatNum( round( $seconds ) );
4796  $s = $secondsMsg->params( $s )->text();
4797  } elseif ( round( $seconds ) < 3600 ) {
4798  $minutes = floor( $seconds / 60 );
4799  $secondsPart = round( fmod( $seconds, 60 ) );
4800  if ( $secondsPart == 60 ) {
4801  $secondsPart = 0;
4802  $minutes++;
4803  }
4804  $s = $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4805  $s .= ' ';
4806  $s .= $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4807  } elseif ( round( $seconds ) <= 2 * 86400 ) {
4808  $hours = floor( $seconds / 3600 );
4809  $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
4810  $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
4811  if ( $secondsPart == 60 ) {
4812  $secondsPart = 0;
4813  $minutes++;
4814  }
4815  if ( $minutes == 60 ) {
4816  $minutes = 0;
4817  $hours++;
4818  }
4819  $s = $hoursMsg->params( $this->formatNum( $hours ) )->text();
4820  $s .= ' ';
4821  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4822  if ( !in_array( $format['avoid'], [ 'avoidseconds', 'avoidminutes' ] ) ) {
4823  $s .= ' ' . $secondsMsg->params( $this->formatNum( $secondsPart ) )->text();
4824  }
4825  } else {
4826  $days = floor( $seconds / 86400 );
4827  if ( $format['avoid'] === 'avoidminutes' ) {
4828  $hours = round( ( $seconds - $days * 86400 ) / 3600 );
4829  if ( $hours == 24 ) {
4830  $hours = 0;
4831  $days++;
4832  }
4833  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4834  $s .= ' ';
4835  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4836  } elseif ( $format['avoid'] === 'avoidseconds' ) {
4837  $hours = floor( ( $seconds - $days * 86400 ) / 3600 );
4838  $minutes = round( ( $seconds - $days * 86400 - $hours * 3600 ) / 60 );
4839  if ( $minutes == 60 ) {
4840  $minutes = 0;
4841  $hours++;
4842  }
4843  if ( $hours == 24 ) {
4844  $hours = 0;
4845  $days++;
4846  }
4847  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4848  $s .= ' ';
4849  $s .= $hoursMsg->params( $this->formatNum( $hours ) )->text();
4850  $s .= ' ';
4851  $s .= $minutesMsg->params( $this->formatNum( $minutes ) )->text();
4852  } else {
4853  $s = $daysMsg->params( $this->formatNum( $days ) )->text();
4854  $s .= ' ';
4855  $s .= $this->formatTimePeriod( $seconds - $days * 86400, $format );
4856  }
4857  }
4858  return $s;
4859  }
4860 
4872  function formatBitrate( $bps ) {
4873  return $this->formatComputingNumbers( $bps, 1000, "bitrate-$1bits" );
4874  }
4875 
4882  function formatComputingNumbers( $size, $boundary, $messageKey ) {
4883  if ( $size <= 0 ) {
4884  return str_replace( '$1', $this->formatNum( $size ),
4885  $this->getMessageFromDB( str_replace( '$1', '', $messageKey ) )
4886  );
4887  }
4888  $sizes = [ '', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zeta', 'yotta' ];
4889  $index = 0;
4890 
4891  $maxIndex = count( $sizes ) - 1;
4892  while ( $size >= $boundary && $index < $maxIndex ) {
4893  $index++;
4894  $size /= $boundary;
4895  }
4896 
4897  // For small sizes no decimal places necessary
4898  $round = 0;
4899  if ( $index > 1 ) {
4900  // For MB and bigger two decimal places are smarter
4901  $round = 2;
4902  }
4903  $msg = str_replace( '$1', $sizes[$index], $messageKey );
4904 
4905  $size = round( $size, $round );
4906  $text = $this->getMessageFromDB( $msg );
4907  return str_replace( '$1', $this->formatNum( $size ), $text );
4908  }
4909 
4920  function formatSize( $size ) {
4921  return $this->formatComputingNumbers( $size, 1024, "size-$1bytes" );
4922  }
4923 
4933  function specialList( $page, $details, $oppositedm = true ) {
4934  if ( !$details ) {
4935  return $page;
4936  }
4937 
4938  $dirmark = ( $oppositedm ? $this->getDirMark( true ) : '' ) . $this->getDirMark();
4939  return $page .
4940  $dirmark .
4941  $this->msg( 'word-separator' )->escaped() .
4942  $this->msg( 'parentheses' )->rawParams( $details )->escaped();
4943  }
4944 
4955  public function viewPrevNext( Title $title, $offset, $limit,
4956  array $query = [], $atend = false
4957  ) {
4958  // @todo FIXME: Why on earth this needs one message for the text and another one for tooltip?
4959 
4960  # Make 'previous' link
4961  $prev = wfMessage( 'prevn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4962  if ( $offset > 0 ) {
4963  $plink = $this->numLink( $title, max( $offset - $limit, 0 ), $limit,
4964  $query, $prev, 'prevn-title', 'mw-prevlink' );
4965  } else {
4966  $plink = htmlspecialchars( $prev );
4967  }
4968 
4969  # Make 'next' link
4970  $next = wfMessage( 'nextn' )->inLanguage( $this )->title( $title )->numParams( $limit )->text();
4971  if ( $atend ) {
4972  $nlink = htmlspecialchars( $next );
4973  } else {
4974  $nlink = $this->numLink( $title, $offset + $limit, $limit,
4975  $query, $next, 'nextn-title', 'mw-nextlink' );
4976  }
4977 
4978  # Make links to set number of items per page
4979  $numLinks = [];
4980  foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
4981  $numLinks[] = $this->numLink( $title, $offset, $num,
4982  $query, $this->formatNum( $num ), 'shown-title', 'mw-numlink' );
4983  }
4984 
4985  return wfMessage( 'viewprevnext' )->inLanguage( $this )->title( $title
4986  )->rawParams( $plink, $nlink, $this->pipeList( $numLinks ) )->escaped();
4987  }
4988 
5001  private function numLink( Title $title, $offset, $limit, array $query, $link,
5002  $tooltipMsg, $class
5003  ) {
5004  $query = [ 'limit' => $limit, 'offset' => $offset ] + $query;
5005  $tooltip = wfMessage( $tooltipMsg )->inLanguage( $this )->title( $title )
5006  ->numParams( $limit )->text();
5007 
5008  return Html::element( 'a', [ 'href' => $title->getLocalURL( $query ),
5009  'title' => $tooltip, 'class' => $class ], $link );
5010  }
5011 
5017  public function getConvRuleTitle() {
5018  return $this->mConverter->getConvRuleTitle();
5019  }
5020 
5026  public function getCompiledPluralRules() {
5027  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
5028  $fallbacks = self::getFallbacksFor( $this->mCode );
5029  if ( !$pluralRules ) {
5030  foreach ( $fallbacks as $fallbackCode ) {
5031  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
5032  if ( $pluralRules ) {
5033  break;
5034  }
5035  }
5036  }
5037  return $pluralRules;
5038  }
5039 
5045  public function getPluralRules() {
5046  $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
5047  $fallbacks = self::getFallbacksFor( $this->mCode );
5048  if ( !$pluralRules ) {
5049  foreach ( $fallbacks as $fallbackCode ) {
5050  $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
5051  if ( $pluralRules ) {
5052  break;
5053  }
5054  }
5055  }
5056  return $pluralRules;
5057  }
5058 
5064  public function getPluralRuleTypes() {
5065  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
5066  $fallbacks = self::getFallbacksFor( $this->mCode );
5067  if ( !$pluralRuleTypes ) {
5068  foreach ( $fallbacks as $fallbackCode ) {
5069  $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
5070  if ( $pluralRuleTypes ) {
5071  break;
5072  }
5073  }
5074  }
5075  return $pluralRuleTypes;
5076  }
5077 
5083  public function getPluralRuleIndexNumber( $number ) {
5084  $pluralRules = $this->getCompiledPluralRules();
5085  $form = Evaluator::evaluateCompiled( $number, $pluralRules );
5086  return $form;
5087  }
5088 
5097  public function getPluralRuleType( $number ) {
5098  $index = $this->getPluralRuleIndexNumber( $number );
5099  $pluralRuleTypes = $this->getPluralRuleTypes();
5100  if ( isset( $pluralRuleTypes[$index] ) ) {
5101  return $pluralRuleTypes[$index];
5102  } else {
5103  return 'other';
5104  }
5105  }
5106 }
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:2444
Language\getCodeFromFileName
static getCodeFromFileName( $filename, $prefix='Language', $suffix='.php')
Get the language code from a file name.
Definition: Language.php:4525
Language\getExtraHashOptions
getExtraHashOptions()
returns language specific options used by User::getPageRenderHash() for example, the preferred langua...
Definition: Language.php:4369
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:2770
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
Language\parseFormattedNumber
parseFormattedNumber( $number)
Definition: Language.php:3358
Language\getURLVariant
getURLVariant()
Definition: Language.php:4343
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:4728
Language\linkPrefixExtension
linkPrefixExtension()
To allow "foo[[bar]]" to extend the link over the whole word "foobar".
Definition: Language.php:3208
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:3876
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:3505
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:3518
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:4920
Language\separatorTransformTable
separatorTransformTable()
Definition: Language.php:3462
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:4211
Language\hasVariants
hasVariants()
Check if this is a language with variants.
Definition: Language.php:4279
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:4098
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
Language\minimumGroupingDigits
minimumGroupingDigits()
Definition: Language.php:3469
Language\segmentByWord
segmentByWord( $string)
Some languages such as Chinese require word segmentation, Specify such segmentation when overridden i...
Definition: Language.php:2904
Language\iconv
iconv( $in, $out, $string)
Definition: Language.php:2671
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:4248
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:3554
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:4390
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:3898
$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:3598
Language\$mMonthGenMsgs
static $mMonthGenMsgs
Definition: Language.php:109
Language\timeanddate
timeanddate( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2348
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:4329
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:2688
$digitGroupingPattern
$digitGroupingPattern
Definition: MessagesAs.php:167
Language\recodeForEdit
recodeForEdit( $s)
Definition: Language.php:3034
Language\markNoConversion
markNoConversion( $text, $noParse=false)
Prepare external link text for conversion.
Definition: Language.php:4410
User\getDatePreference
getDatePreference()
Get the user's preferred date format.
Definition: User.php:3494
Language\getDefaultVariant
getDefaultVariant()
Definition: Language.php:4336
Language\fallback8bitEncoding
fallback8bitEncoding()
Definition: Language.php:2881
Language\embedBidi
embedBidi( $text='')
Wraps argument with unicode control characters for directionality safety.
Definition: Language.php:4121
Language\formatTimePeriod
formatTimePeriod( $seconds, $format=[])
Formats a time given in seconds into a string representation of that time.
Definition: Language.php:4772
Language\formatBitrate
formatBitrate( $bps)
Format a bitrate for output, using an appropriate unit (bps, kbps, Mbps, Gbps, Tbps,...
Definition: Language.php:4872
Language\equals
equals(Language $lang)
Compare with an other language object.
Definition: Language.php:4474
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:3277
Language\getMessagesFileName
static getMessagesFileName( $code)
Definition: Language.php:4568
Language\getPluralRuleTypes
getPluralRuleTypes()
Get the plural rule types for the language.
Definition: Language.php:5064
Language\capitalizeAllNouns
capitalizeAllNouns()
Definition: Language.php:3175
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:4933
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:2867
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:2751
Language\initEncoding
initEncoding()
Definition: Language.php:3024
Language\formatExpiry
formatExpiry( $expiry, $format=true, $infinity='infinity')
Decode an expiry (block, protection, etc) which has come from the DB.
Definition: Language.php:4742
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:3296
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:2393
Language\caseFold
caseFold( $s)
Return a case-folded representation of $s.
Definition: Language.php:2858
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:2529
Language\segmentForDiff
segmentForDiff( $text)
languages like Chinese need to be segmented in order for the diff to be of any use
Definition: Language.php:4191
Language\classFromCode
static classFromCode( $code, $fallback=true)
Definition: Language.php:4540
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:4380
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:4955
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:4201
Language\autoConvert
autoConvert( $text, $variant=false)
convert text to a variant
Definition: Language.php:4223
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:3735
Language\normalize
normalize( $s)
Convert a UTF-8 string to normal form C.
Definition: Language.php:3060
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:3530
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:3223
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
Language\convertForSearchResult
convertForSearchResult( $termsArray)
Definition: Language.php:2957
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:4687
$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:3481
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:4500
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:2483
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:3262
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:3574
Language\getAllMessages
getAllMessages()
Definition: Language.php:2661
Language\getMagic
getMagic( $mw)
Fill a MagicWord object with data from here.
Definition: Language.php:3236
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:3099
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:2506
$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:5083
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:2927
Language\getCompiledPluralRules
getCompiledPluralRules()
Get the compiled plural rules for the language.
Definition: Language.php:5026
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:2585
Language\convertCategoryKey
convertCategoryKey( $key)
Definition: Language.php:4312
Language\digitGroupingPattern
digitGroupingPattern()
Definition: Language.php:3448
Language\ucwords
ucwords( $str)
Definition: Language.php:2794
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:5045
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:3186
Language\uc
uc( $str, $first=false)
Convert a string to uppercase.
Definition: Language.php:2735
Language\$mVariants
$mVariants
Definition: Language.php:60
Language\translateBlockExpiry
translateBlockExpiry( $str, User $user=null, $now=0)
Definition: Language.php:4148
or
or
Definition: COPYING.txt:140
Language\formatNumNoSeparators
formatNumNoSeparators( $number)
Front-end for non-commafied formatNum.
Definition: Language.php:3350
$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:3624
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:2367
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:4436
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:2221
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:4359
Language\fixVariableInNamespace
fixVariableInNamespace( $talk)
Definition: Language.php:4707
$value
$value
Definition: styleTest.css.php:49
Language\ucwordbreaks
ucwordbreaks( $str)
capitalize words at word breaks
Definition: Language.php:2818
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:4486
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:5097
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:2244
$wgMetaNamespaceTalk
$wgMetaNamespaceTalk
Name of the project talk namespace.
Definition: DefaultSettings.php:3848
Language\ucwordsCallbackMB
ucwordsCallbackMB( $matches)
Definition: Language.php:2704
$wgLanguageCode
$wgLanguageCode
Site language code.
Definition: DefaultSettings.php:2942
Language\hebrewNumeral
static hebrewNumeral( $num)
Hebrew Gematria number formatting up to 9999.
Definition: Language.php:2079
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:4675
Language\ucwordbreaksCallbackMB
ucwordbreaksCallbackMB( $matches)
Definition: Language.php:2696
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:4077
$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:2044
Language\getFileName
static getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: Language.php:4556
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:3044
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:434
Language\getDirMark
getDirMark( $opposite=false)
A hidden direction mark (LRM or RLM), depending on the language direction.
Definition: Language.php:3163
Language\normalizeForSearch
normalizeForSearch( $string)
Some languages have special punctuation need to be normalized.
Definition: Language.php:2915
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:2166
Language\formatComputingNumbers
formatComputingNumbers( $size, $boundary, $messageKey)
Definition: Language.php:4882
Language\convertHtml
convertHtml( $text, $isTitle=false)
Perform output conversion on a string, and encode for safe HTML output.
Definition: Language.php:4304
Language\getFallbackFor
static getFallbackFor( $code)
Get the first fallback for a given language.
Definition: Language.php:4598
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:4447
Language\$dateFormatStrings
$dateFormatStrings
Definition: Language.php:64
Language\hasWordBreaks
hasWordBreaks()
Most writing systems use whitespace to break up words.
Definition: Language.php:2893
Language\getDir
getDir()
Return the correct HTML 'dir' attribute value for this language.
Definition: Language.php:3107
Language\date
date( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2309
Language\alignEnd
alignEnd()
Return 'right' or 'left' as appropriate alignment for line-end for this language's text direction.
Definition: Language.php:3131
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:3853
Language\setCode
setCode( $code)
Definition: Language.php:4511
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:4322
Language\convertPlural
convertPlural( $count, $forms)
Plural form transformations, needed for some languages.
Definition: Language.php:4047
$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:2044
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:4644
$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:3949
Language\removeBadCharFirst
removeBadCharFirst( $string)
Remove bytes that represent an incomplete Unicode character at the start of string (e....
Definition: Language.php:3709
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:2654
Language\getGrammarTransformations
getGrammarTransformations()
Get the grammar transformations data for the language.
Definition: Language.php:3969
Language\getMessageKeysFor
static getMessageKeysFor( $code)
Get all message keys for a given language.
Definition: Language.php:4699
Language\getJsonMessagesFileName
static getJsonMessagesFileName( $code)
Definition: Language.php:4581
Language\firstChar
firstChar( $s)
Get the first character of a string.
Definition: Language.php:2969
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:3098
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:3085
Language\removeBadCharLast
removeBadCharLast( $string)
Remove bytes that represent an incomplete Unicode character at the end of string (e....
Definition: Language.php:3683
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:2044
Language\romanNumeral
static romanNumeral( $num)
Roman number formatting up to 10000.
Definition: Language.php:2048
Language\getConvRuleTitle
getConvRuleTitle()
Get the conversion rule title, if any.
Definition: Language.php:5017
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:2548
$t
$t
Definition: testCompression.php:69
Language\time
time( $ts, $adj=false, $format=true, $timecorrection=false)
Definition: Language.php:2328
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:2947
Language\isMultibyte
isMultibyte( $str)
Definition: Language.php:2786
Language\alignStart
alignStart()
Return 'left' or 'right' as appropriate alignment for line-start for this language's text direction.
Definition: Language.php:3119
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:3455
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:3146
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:2031
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:2275
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:3216
Language\hasVariant
hasVariant( $variant)
Strict check if the language has the specific variant.
Definition: Language.php:4293
Language\numLink
numLink(Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class)
Helper function for viewPrevNext() that generates links.
Definition: Language.php:5001
Language\__destruct
__destruct()
Reduce memory usage.
Definition: Language.php:477
Language\commafy
commafy( $number)
Adds commas to a given number.
Definition: Language.php:3383
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:4616
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:4233
Language\linkTrail
linkTrail()
A regular expression to match legal word-trailing characters which should be merged onto a link of th...
Definition: Language.php:4426
$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:2715
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:4018
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:3322
$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:4270
Language\convertTitle
convertTitle( $title)
Convert a Title object to a string in the preferred variant.
Definition: Language.php:4258