3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
21 namespace MediaWiki\Specials
;
23 use MediaWiki\Languages\LanguageNameUtils
;
24 use MediaWiki\Page\RedirectLookup
;
25 use MediaWiki\SpecialPage\RedirectSpecialArticle
;
26 use MediaWiki\Title\Title
;
29 * Redirect to the appropriate translated version of a page if it exists.
31 * Usage: [[Special:MyLanguage/Page name|link text]]
34 * @ingroup SpecialPage
35 * @author Niklas Laxström
36 * @author Siebrand Mazeland
37 * @copyright Copyright © 2010-2013 Niklas Laxström, Siebrand Mazeland
39 class SpecialMyLanguage
extends RedirectSpecialArticle
{
41 private LanguageNameUtils
$languageNameUtils;
42 private RedirectLookup
$redirectLookup;
44 public function __construct(
45 LanguageNameUtils
$languageNameUtils,
46 RedirectLookup
$redirectLookup
48 parent
::__construct( 'MyLanguage' );
49 $this->languageNameUtils
= $languageNameUtils;
50 $this->redirectLookup
= $redirectLookup;
54 * If the special page is a redirect, then get the Title object it redirects to.
57 * @param string|null $subpage
60 public function getRedirect( $subpage ) {
61 $title = $this->findTitle( $subpage );
62 // Go to the main page if given invalid title.
64 $title = Title
::newMainPage();
72 * This may return the base page, e.g. if the UI and
73 * content language are the same.
75 * Examples, assuming the UI language is fi and the content language
77 * - input Page: returns Page/fi if it exists, otherwise Page
78 * - input Page/de: returns Page/fi if it exists, otherwise Page/de
79 * if it exists, otherwise Page
81 * @param string|null $subpage
84 public function findTitle( $subpage ) {
85 return $this->findTitleInternal( $subpage, false );
89 * Find a title for transclusion. This avoids returning the base
90 * page if a suitable alternative exists.
92 * Examples, assuming the UI language is fi and the content language
94 * - input Page: returns Page/fi if it exists, otherwise Page/en if
95 * it exists, otherwise Page
96 * - input Page/de: returns Page/fi if it exists, otherwise Page/de
97 * if it exists, otherwise Page/en if it exists, otherwise Page
99 * @param string|null $subpage
102 public function findTitleForTransclusion( $subpage ) {
103 return $this->findTitleInternal( $subpage, true );
107 * Find a title, depending on the content language and the user's
108 * interface language.
110 * @param string|null $subpage
111 * @param bool $forTransclusion
114 private function findTitleInternal( $subpage, $forTransclusion ) {
115 // base = title without the language code suffix
116 // provided = the title as it was given
117 $base = $provided = null;
118 if ( $subpage !== null ) {
119 $provided = Title
::newFromText( $subpage );
122 if ( $provided && str_contains( $subpage, '/' ) ) {
123 $pos = strrpos( $subpage, '/' );
124 $basepage = substr( $subpage, 0, $pos );
125 $code = substr( $subpage, $pos +
1 );
126 if ( strlen( $code ) && $this->languageNameUtils
->isKnownLanguageTag( $code ) ) {
127 $base = Title
::newFromText( $basepage );
132 if ( !$base ||
!$base->canExist() ) {
133 // No subpage provided or base page does not exist
138 if ( $base->isRedirect() ) {
139 $target = $this->redirectLookup
->getRedirectTarget( $base );
140 if ( $target !== null ) {
141 $base = Title
::newFromLinkTarget( $target );
142 // Preserve the fragment from the redirect target
143 $fragment = $base->getFragment();
147 $uiLang = $this->getLanguage();
148 $baseLang = $base->getPageLanguage();
150 // T309329: Always use subpages for transclusion
151 if ( !$forTransclusion && $baseLang->equals( $uiLang ) ) {
152 // Short circuit when the current UI language is the
153 // page's content language to avoid unnecessary page lookups.
157 // Check for a subpage in the current UI language
158 $proposed = $base->getSubpage( $uiLang->getCode() );
159 if ( $proposed && $proposed->exists() ) {
160 if ( $fragment !== '' ) {
161 $proposed->setFragment( $fragment );
166 // Explicit language code given and the page exists
167 if ( $provided !== $base && $provided->exists() ) {
168 // Not based on the redirect target, don't need the fragment
172 // Check for fallback languages specified by the UI language
173 $possibilities = $uiLang->getFallbackLanguages();
174 foreach ( $possibilities as $lang ) {
175 // $base already include fragments
176 // T309329: Always use subpages for transclusion
177 // T333187: Do not ignore base language page if matched
178 if ( !$forTransclusion && $lang === $baseLang->getCode() ) {
181 // Look for subpages if is for transclusion or didn't match base page language
182 $proposed = $base->getSubpage( $lang );
183 if ( $proposed && $proposed->exists() ) {
184 if ( $fragment !== '' ) {
185 $proposed->setFragment( $fragment );
191 // When all else has failed, return the base page
196 * Target can identify a specific user's language preference.
202 public function personallyIdentifiableTarget() {
208 * Retain the old class name for backwards compatibility.
209 * @deprecated since 1.41
211 class_alias( SpecialMyLanguage
::class, 'SpecialMyLanguage' );