Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 77 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
LanguageBabelBox | |
0.00% |
0 / 77 |
|
0.00% |
0 / 7 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
render | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
6 | |||
getText | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
6 | |||
addCategories | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
addCategory | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
42 | |||
getCategoryName | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
42 | |||
getCategoryLink | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * Contains code for language boxes. |
4 | * |
5 | * @file |
6 | * @author Robert Leverington |
7 | * @author Robin Pepermans |
8 | * @author Niklas Laxström |
9 | * @author Brian Wolff |
10 | * @author Purodha Blissenbach |
11 | * @author Sam Reed |
12 | * @author Siebrand Mazeland |
13 | * @license GPL-2.0-or-later |
14 | */ |
15 | |
16 | declare( strict_types = 1 ); |
17 | |
18 | namespace MediaWiki\Babel\BabelBox; |
19 | |
20 | use LanguageCode; |
21 | use MediaWiki\Babel\BabelAutoCreate; |
22 | use MediaWiki\Babel\BabelLanguageCodes; |
23 | use MediaWiki\MediaWikiServices; |
24 | use MediaWiki\Title\Title; |
25 | use ParserOutput; |
26 | |
27 | /** |
28 | * Class for babel language boxes. |
29 | */ |
30 | class LanguageBabelBox implements BabelBox { |
31 | |
32 | /** |
33 | * @var Title |
34 | */ |
35 | private $title; |
36 | |
37 | /** |
38 | * @var string |
39 | */ |
40 | private $code; |
41 | |
42 | /** |
43 | * @var string |
44 | */ |
45 | private $level; |
46 | |
47 | /** |
48 | * @var bool |
49 | */ |
50 | private $createCategories; |
51 | |
52 | /** |
53 | * Construct a babel box for the given language and level. |
54 | * |
55 | * @param Title $title |
56 | * @param string $code Language code to use. |
57 | * This is a mediawiki-internal code (not necessarily a valid BCP-47 code) |
58 | * @param string $level Level of ability to use. |
59 | */ |
60 | public function __construct( |
61 | Title $title, |
62 | string $code, |
63 | string $level |
64 | ) { |
65 | $this->title = $title; |
66 | $this->code = BabelLanguageCodes::getCode( $code ) ?? $code; |
67 | $this->level = $level; |
68 | } |
69 | |
70 | /** |
71 | * Return the babel box code. |
72 | * |
73 | * @return string A babel box for the given language and level. |
74 | */ |
75 | public function render(): string { |
76 | $code = $this->code; |
77 | $catCode = BabelLanguageCodes::getCategoryCode( $code ); |
78 | $bcp47 = LanguageCode::bcp47( $code ); |
79 | |
80 | $portal = wfMessage( 'babel-portal', $catCode )->inContentLanguage()->text(); |
81 | if ( $portal !== '' ) { |
82 | $portal = "[[$portal|$catCode]]"; |
83 | } else { |
84 | $portal = $catCode; |
85 | } |
86 | $header = "$portal<span class=\"mw-babel-box-level-{$this->level}\">-{$this->level}</span>"; |
87 | |
88 | $name = BabelLanguageCodes::getName( $code ); |
89 | $text = self::getText( $this->title, $name, $code, $this->level ); |
90 | |
91 | $dir_current = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( $code )->getDir(); |
92 | |
93 | $dir_head = $this->title->getPageLanguage()->getDir(); |
94 | |
95 | return <<<EOT |
96 | <div class="mw-babel-box mw-babel-box-{$this->level} mw-babel-box-{$catCode}" dir="$dir_head"> |
97 | {| |
98 | ! dir="$dir_head" | $header |
99 | | dir="$dir_current" lang="$bcp47" | $text |
100 | |} |
101 | </div> |
102 | EOT; |
103 | } |
104 | |
105 | /** |
106 | * Get the text to display in the language box for specific language and |
107 | * level. If MediaWiki:Babel-<level>-n (the message that includes the |
108 | * language autonym) is translated into the given language, use that |
109 | * otherwise use MediaWiki:Babel-<level> (the message that takes the |
110 | * language name as a parameter) |
111 | * |
112 | * @param Title $title |
113 | * @param string $name |
114 | * @param string $code Mediawiki-internal language code of language to use. |
115 | * @param string $level Level to use. |
116 | * @return string Text for display, in wikitext format. |
117 | */ |
118 | private static function getText( |
119 | Title $title, |
120 | string $name, |
121 | string $code, |
122 | string $level |
123 | ): string { |
124 | $categoryLevel = self::getCategoryLink( $title, $level, $code ); |
125 | $categoryMain = self::getCategoryLink( $title, null, $code ); |
126 | |
127 | // Give grep a chance to find the usages: |
128 | // babel-0-n, babel-1-n, babel-2-n, babel-3-n, babel-4-n, babel-5-n, babel-N-n |
129 | $text = wfMessage( "babel-$level-n", |
130 | $categoryLevel, $categoryMain, '', $title->getDBkey() |
131 | )->inLanguage( $code )->text(); |
132 | |
133 | $fallbackLanguage = MediaWikiServices::getInstance()->getLanguageFallback()->getFirst( $code ); |
134 | // Because of T75473, the above wfMessage call will ignore any |
135 | // MediaWiki namespace overrides for fallback languages. Hence, we |
136 | // must explicitly ignore them here, or else the comparison will fail, |
137 | // resulting in a message claiming that the user knows the fallback |
138 | // language (probably English), rather than the language |
139 | // they actually specified. |
140 | $fallback = wfMessage( "babel-$level-n", |
141 | $categoryLevel, $categoryMain, '', $title->getDBkey() |
142 | )->useDatabase( false )->inLanguage( $fallbackLanguage ?? $code )->text(); |
143 | |
144 | // Give grep a chance to find the usages: |
145 | // babel-0, babel-1, babel-2, babel-3, babel-4, babel-5, babel-N |
146 | if ( $text == $fallback ) { |
147 | $text = wfMessage( "babel-$level", |
148 | $categoryLevel, $categoryMain, $name, $title->getDBkey() |
149 | )->inLanguage( $code )->text(); |
150 | } |
151 | |
152 | return $text; |
153 | } |
154 | |
155 | /** |
156 | * Add appropriate categories for the language box to the given parser output |
157 | * |
158 | * @param ParserOutput $parserOutput Output to add categories to |
159 | */ |
160 | public function addCategories( ParserOutput $parserOutput ): void { |
161 | global $wgBabelCategorizeNamespaces; |
162 | |
163 | if ( |
164 | $wgBabelCategorizeNamespaces !== null && |
165 | !$this->title->inNamespaces( $wgBabelCategorizeNamespaces ) |
166 | ) { |
167 | return; |
168 | } |
169 | |
170 | # Add main category |
171 | if ( $this->level !== '0' ) { |
172 | self::addCategory( $parserOutput, $this->code, null, $this->level ); |
173 | } |
174 | |
175 | # Add level category |
176 | self::addCategory( $parserOutput, $this->code, $this->level, false ); |
177 | } |
178 | |
179 | /** |
180 | * Adds one category to the given ParserOutput, and arranges for its creation if it doesn't exist. |
181 | * |
182 | * @param ParserOutput $parserOutput Parser output to use |
183 | * @param string $code Code of language that the category is for. |
184 | * @param string|null $level Level that the category is for. |
185 | * @param string|bool $sortkey The sortkey to use for the category, or false to use the default sort |
186 | */ |
187 | private function addCategory( ParserOutput $parserOutput, |
188 | string $code, ?string $level, $sortkey |
189 | ) { |
190 | global $wgBabelAutoCreate; |
191 | $isOverridden = false; |
192 | $category = self::getCategoryName( $level, $code, $isOverridden ); |
193 | if ( $category === null ) { |
194 | return; |
195 | } |
196 | if ( $sortkey === false ) { |
197 | $sortkey = $parserOutput->getPageProperty( 'defaultsort' ); |
198 | } |
199 | $parserOutput->addCategory( $category, $sortkey ?? '' ); |
200 | |
201 | if ( $wgBabelAutoCreate ) { |
202 | // Now arrange for autocreation (in LinksUpdate hook) unless the category was overridden locally |
203 | // (to reduce the risk if a compromised admin edits MediaWiki:Babel-category-override) |
204 | $title = Title::makeTitleSafe( NS_CATEGORY, $category ); |
205 | $text = BabelAutoCreate::getCategoryText( $code, $level ); |
206 | if ( !$isOverridden && !$title->exists() ) { |
207 | $parserOutput->appendExtensionData( "babel-tocreate", $category ); |
208 | $parserOutput->setExtensionData( "babel-category-text-$category", $text ); |
209 | } |
210 | } |
211 | } |
212 | |
213 | /** |
214 | * Replace the placeholder variables from the category names configuration |
215 | * array with actual values. |
216 | * |
217 | * @param ?string $level Level of babel category in question, or null for the main category |
218 | * @param string $code Mediawiki-internal language code of category. |
219 | * @param bool &$isOverridden Output parameter. Set to true if the category is overridden on-wiki |
220 | * so that the caller knows not to create categories. |
221 | * @return string|null Category name with variables replaced and possibly |
222 | * overridden by the wiki, or null if no category is desired. |
223 | */ |
224 | private static function getCategoryName( ?string $level, string $code, bool &$isOverridden ): ?string { |
225 | global $wgLanguageCode, $wgBabelAllowOverride, $wgBabelMainCategory, $wgBabelCategoryNames; |
226 | |
227 | $categoryDef = $level !== null ? $wgBabelCategoryNames[$level] : $wgBabelMainCategory; |
228 | if ( $categoryDef === false ) { |
229 | return null; |
230 | } |
231 | |
232 | $category = strtr( $categoryDef, [ |
233 | '%code%' => BabelLanguageCodes::getCategoryCode( $code ), |
234 | '%wikiname%' => BabelLanguageCodes::getName( $code, $wgLanguageCode ), |
235 | '%nativename%' => BabelLanguageCodes::getName( $code ) |
236 | ] ); |
237 | |
238 | $oldCategory = $category; |
239 | |
240 | // Chance to locally override categorization |
241 | if ( $wgBabelAllowOverride ) { |
242 | $category = wfMessage( "babel-category-override", |
243 | $category, $code, $level |
244 | )->inContentLanguage()->text(); |
245 | if ( $category !== $oldCategory ) { |
246 | $isOverridden = true; |
247 | } |
248 | } |
249 | |
250 | // Normalize using Title |
251 | $title = Title::makeTitleSafe( NS_CATEGORY, $category ); |
252 | if ( !$title ) { |
253 | // babel-category-override return an invalid page name |
254 | return null; |
255 | } |
256 | |
257 | return $title->getDBkey(); |
258 | } |
259 | |
260 | /** |
261 | * Returns the right link target for a category (either the category itself or the |
262 | * title given to get a self-link) |
263 | * @param Title $title |
264 | * @param ?string $level Level of babel category in question, or null for the main category |
265 | * @param string $code Mediawiki-internal language code of category. |
266 | * @return string Link target to use for the given category |
267 | */ |
268 | private static function getCategoryLink( Title $title, ?string $level, string $code ): string { |
269 | $isOverridden = false; |
270 | $category = self::getCategoryName( $level, $code, $isOverridden ); |
271 | if ( $category !== null ) { |
272 | return ":Category:" . $category; |
273 | } |
274 | return ":" . $title->getFullText(); |
275 | } |
276 | } |