Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslationsSpecialPage.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorInterface;
5
8use MediaWiki\Html\Html;
9use MediaWiki\HTMLForm\HTMLForm;
10use MediaWiki\Language\Language;
11use MediaWiki\Language\LanguageNameUtils;
12use MediaWiki\Message\Message;
13use MediaWiki\Revision\RevisionRecord;
14use MediaWiki\Revision\SlotRecord;
15use MediaWiki\Search\SearchEngineFactory;
16use MediaWiki\SpecialPage\IncludableSpecialPage;
17use MediaWiki\Title\Title;
18use MediaWiki\Utils\MWTimestamp;
19use UnexpectedValueException;
20use Wikimedia\HtmlArmor\HtmlArmor;
21use Wikimedia\Rdbms\IConnectionProvider;
22
32class TranslationsSpecialPage extends IncludableSpecialPage {
33
34 public function __construct(
35 private readonly Language $contentLanguage,
36 private readonly LanguageNameUtils $languageNameUtils,
37 private readonly IConnectionProvider $dbProvider,
38 private readonly SearchEngineFactory $searchEngineFactory,
39 ) {
40 parent::__construct( 'Translations' );
41 }
42
43 protected function getGroupName(): string {
44 return 'translation';
45 }
46
48 public function getDescription() {
49 return $this->msg( 'translations' );
50 }
51
53 public function prefixSearchSubpages( $search, $limit, $offset ) {
54 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
55 }
56
62 public function execute( $par ) {
63 $this->setHeaders();
64 $this->outputHeader();
65
66 $out = $this->getOutput();
67 $out->addModuleStyles( 'ext.translate.specialpages.styles' );
68
69 $par = (string)$par;
70
71 if ( $this->including() ) {
72 $title = Title::newFromText( $par );
73 if ( !$title ) {
74 $out->addWikiMsg( 'translate-translations-including-no-param' );
75 } else {
76 $this->showTranslations( $title );
77 }
78
79 return;
80 }
81
85 $request = $this->getRequest();
86 $message = $request->getText( 'message', $par );
87 $namespace = $request->getInt( 'namespace', NS_MAIN );
88
89 $title = Title::newFromText( $message, $namespace );
90
91 $out->addHelpLink(
92 'Help:Extension:Translate/Statistics_and_reporting#Translations_in_all_languages'
93 );
94
95 if ( !$title ) {
96 $title = Title::makeTitle( NS_MEDIAWIKI, '' );
97 $this->namespaceMessageForm( $title );
98 } else {
99 $this->namespaceMessageForm( $title );
100 $out->addHTML( '<br />' );
101 $this->showTranslations( $title );
102 }
103 }
104
108 private function namespaceMessageForm( Title $title ): void {
109 $formDescriptor = [
110 'textbox' => [
111 'type' => 'text',
112 'name' => 'message',
113 'id' => 'message',
114 'label-message' => 'translate-translations-messagename',
115 'size' => 30,
116 'default' => $title->getText(),
117 ],
118 'selector' => [
119 'type' => 'select',
120 'name' => 'namespace',
121 'id' => 'namespace',
122 'label-message' => 'translate-translations-project',
123 'options' => $this->getSortedNamespaces(),
124 'default' => $title->getNamespace(),
125 ]
126 ];
127
128 HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
129 ->setMethod( 'get' )
130 ->setTitle( $this->getPageTitle() ) // Remove subpage
131 ->setSubmitTextMsg( 'allpagessubmit' )
132 ->setWrapperLegendMsg( 'translate-translations-fieldset-title' )
133 ->prepareForm()
134 ->displayForm( false );
135 }
136
142 private function getSortedNamespaces(): array {
143 $nslist = [];
144 foreach ( $this->getConfig()->get( 'TranslateMessageNamespaces' ) as $ns ) {
145 $nslist[$this->contentLanguage->getFormattedNsText( $ns )] = $ns;
146 }
147 ksort( $nslist );
148
149 return $nslist;
150 }
151
155 private function showTranslations( Title $title ): void {
156 $handle = new MessageHandle( $title );
157 $namespace = $title->getNamespace();
158
159 if ( !$handle->isValid() ) {
160 $this->getOutput()->addWikiMsg( 'translate-translations-no-message', $title->getPrefixedText() );
161
162 return;
163 }
164
165 $revisions = Utilities::getTranslationRevisions( $handle );
166
167 if ( !$revisions ) {
168 $this->getOutput()->addWikiMsg(
169 'translate-translations-no-message',
170 $title->getPrefixedText()
171 );
172
173 return;
174 } else {
175 $this->getOutput()->addWikiMsg(
176 'translate-translations-count',
177 Message::numParam( count( $revisions ) )
178 );
179 }
180
181 $rows = [
182 Html::rawElement(
183 'tr',
184 [],
185 Html::element( 'th', [], $this->msg( 'allmessagesname' )->text() ) .
186 Html::element( 'th', [], $this->msg( 'allmessagescurrent' )->text() ) .
187 Html::element( 'th', [], $this->msg( 'translate-translations-last-edit-header' )->text() )
188 ),
189 ];
190
191 $historyText = "\u{00A0}<sup>" .
192 $this->msg( 'translate-translations-history-short' )->escaped() .
193 "</sup>\u{00A0}";
194 $separator = $this->msg( 'word-separator' )->plain();
195
196 foreach ( $revisions as $key => $revision ) {
197 $tTitle = Title::makeTitle( $namespace, $key );
198 $tHandle = new MessageHandle( $tTitle );
199
200 $code = $tHandle->getCode();
201
202 $text = Utilities::getLanguageName( $code, $this->getLanguage()->getCode() );
203 $text .= $separator;
204 $text .= $this->msg( 'parentheses' )->params( $code )->plain();
205 $tools = [
206 'edit' => Html::element(
207 'a',
208 [ 'href' => Utilities::getEditorUrl( $tHandle ) ],
209 $text
210 ),
211 'history' => $this->getLinkRenderer()->makeLink(
212 $tTitle,
213 new HtmlArmor( $historyText ),
214 [
215 'title' => $this->msg( 'history-title', $tTitle->getPrefixedDBkey() )->text()
216 ],
217 [ 'action' => 'history' ]
218 ),
219 ];
220
221 $mainContent = $revision->getContent( SlotRecord::MAIN );
222 try {
223 $pageText = Utilities::getTextFromTextContent( $mainContent );
224 } catch ( UnexpectedValueException ) {
225 continue;
226 }
227
228 $class = '';
229 if ( MessageHandle::hasFuzzyString( $pageText ) || $tHandle->isFuzzy() ) {
230 $class = 'mw-sp-translate-fuzzy';
231 }
232
233 $languageAttributes = [];
234 if ( $this->languageNameUtils->isKnownLanguageTag( $code ) ) {
235 $language = $tHandle->getEffectiveLanguage();
236 $languageAttributes = [
237 'lang' => $language->getHtmlCode(),
238 'dir' => $language->getDir(),
239 ];
240 }
241
242 $formattedContent = Utilities::convertWhiteSpaceToHTML( $pageText );
243
244 // Build the last-edit cell with a sortable Unix timestamp and a linked username.
245 $revTimestamp = $revision->getTimestamp();
246 $unixTimestamp = wfTimestamp( TS_UNIX, $revTimestamp );
247 $humanTimestamp = $this->getLanguage()->getHumanTimestamp( new MWTimestamp( $revTimestamp ) );
248
249 $user = $revision->getUser( RevisionRecord::FOR_PUBLIC );
250 if ( $user !== null ) {
251 $userLinkHtml = $this->getLinkRenderer()->makeUserLink( $user, $this->getContext() );
252 } else {
253 $userLinkHtml = Html::element(
254 'span',
255 [ 'class' => 'history-deleted' ],
256 $this->msg( 'rev-deleted-user' )->text()
257 );
258 }
259
260 $lastEditHtml = $this->msg( 'translate-translations-last-edit' )
261 ->params( $humanTimestamp )
262 ->rawParams( $userLinkHtml )
263 ->escaped();
264
265 $rows[] = Html::rawElement(
266 'tr',
267 [ 'class' => $class ],
268 Html::rawElement( 'td', [], $tools['history'] . $tools['edit'] ) .
269 Html::rawElement( 'td', $languageAttributes, $formattedContent ) .
270 Html::rawElement( 'td', [ 'data-sort-value' => $unixTimestamp ], $lastEditHtml )
271 );
272 }
273
274 $out = Html::rawElement(
275 'table',
276 [ 'class' => 'mw-sp-translate-table sortable wikitable' ],
277 "\n" . implode( "\n", $rows ) . "\n"
278 );
279 $this->getOutput()->addHTML( $out );
280 }
281}
Class for pointing to messages, like Title class is for titles.
Implements a special page which shows all translations for a message.
execute( $par)
Entry point : initialise variables and call subfunctions.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:30