}
}
+ /**
+ * Get the language code from the available languages for this file that matches the language
+ * requested by the user
+ *
+ * @param string $userPreferredLanguage
+ * @return string|null
+ */
+ public function getMatchedLanguage( $userPreferredLanguage ) {
+ $handler = $this->getHandler();
+ if ( $handler && method_exists( $handler, 'getMatchedLanguage' ) ) {
+ return $handler->getMatchedLanguage(
+ $userPreferredLanguage,
+ $handler->getAvailableLanguages( $this )
+ );
+ } else {
+ return null;
+ }
+ }
+
/**
* In files that support multiple language, what is the default language
* to use if none specified.
if ( isset( $metadata['translations'] ) ) {
foreach ( $metadata['translations'] as $lang => $langType ) {
if ( $langType === SVGReader::LANG_FULL_MATCH ) {
- $langList[] = $lang;
+ $langList[] = strtolower( $lang );
}
}
}
}
- return $langList;
+ return array_unique( $langList );
}
/**
- * What language to render file in if none selected.
+ * SVG's systemLanguage matching rules state:
+ * 'The `systemLanguage` attribute ... [e]valuates to "true" if one of the languages indicated
+ * by user preferences exactly equals one of the languages given in the value of this parameter,
+ * or if one of the languages indicated by user preferences exactly equals a prefix of one of
+ * the languages given in the value of this parameter such that the first tag character
+ * following the prefix is "-".'
*
- * @param File $file
- * @return string Language code.
+ * Return the first element of $svgLanguages that matches $userPreferredLanguage
+ *
+ * @see https://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
+ * @param string $userPreferredLanguage
+ * @param array $svgLanguages
+ * @return string|null
+ */
+ public function getMatchedLanguage( $userPreferredLanguage, array $svgLanguages ) {
+ foreach ( $svgLanguages as $svgLang ) {
+ if ( strcasecmp( $svgLang, $userPreferredLanguage ) === 0 ) {
+ return $svgLang;
+ }
+ $trimmedSvgLang = $svgLang;
+ while ( strpos( $trimmedSvgLang, '-' ) !== false ) {
+ $trimmedSvgLang = substr( $trimmedSvgLang, 0, strrpos( $trimmedSvgLang, '-' ) );
+ if ( strcasecmp( $trimmedSvgLang, $userPreferredLanguage ) === 0 ) {
+ return $svgLang;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * What language to render file in if none selected
+ *
+ * @param File $file Language code
+ * @return string
*/
public function getDefaultRenderLanguage( File $file ) {
return 'en';
return ( $value > 0 );
} elseif ( $name == 'lang' ) {
// Validate $code
- if ( $value === '' || !Language::isValidBuiltInCode( $value ) ) {
+ if ( $value === '' || !Language::isValidCode( $value ) ) {
wfDebug( "Invalid user language code\n" );
return false;
public function makeParamString( $params ) {
$lang = '';
if ( isset( $params['lang'] ) && $params['lang'] !== 'en' ) {
- $params['lang'] = strtolower( $params['lang'] );
- $lang = "lang{$params['lang']}-";
+ $lang = 'lang' . strtolower( $params['lang'] ) . '-';
}
if ( !isset( $params['width'] ) ) {
return false;
public function parseParamString( $str ) {
$m = false;
- if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
+ if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/i', $str, $m ) ) {
return [ 'width' => array_pop( $m ), 'lang' => $m[1] ];
} elseif ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
return [ 'width' => $m[1], 'lang' => 'en' ];
return parent::getContentObject();
}
+ private function getLanguageForRendering( WebRequest $request, File $file ) {
+ $handler = $this->displayImg->getHandler();
+
+ $requestLanguage = $request->getVal( 'lang' );
+ if ( !is_null( $requestLanguage ) ) {
+ if ( $handler && $handler->validateParam( 'lang', $requestLanguage ) ) {
+ return $requestLanguage;
+ }
+ }
+
+ return $handler->getDefaultRenderLanguage( $this->displayImg );
+ }
+
protected function openShowImage() {
global $wgEnableUploads, $wgSend404Code, $wgSVGMaxSize;
$params = [ 'page' => $page ];
}
- $renderLang = $request->getVal( 'lang' );
+ $renderLang = $this->getLanguageForRendering( $request, $this->displayImg );
if ( !is_null( $renderLang ) ) {
- $handler = $this->displayImg->getHandler();
- if ( $handler && $handler->validateParam( 'lang', $renderLang ) ) {
- $params['lang'] = $renderLang;
- } else {
- $renderLang = null;
- }
+ $params['lang'] = $renderLang;
}
$width_orig = $this->displayImg->getWidth( $page );
$renderLangOptions = $this->displayImg->getAvailableLanguages();
if ( count( $renderLangOptions ) >= 1 ) {
- $currentLanguage = $renderLang;
- $defaultLang = $this->displayImg->getDefaultRenderLanguage();
- if ( is_null( $currentLanguage ) ) {
- $currentLanguage = $defaultLang;
- }
- $out->addHTML( $this->doRenderLangOpt( $renderLangOptions, $currentLanguage, $defaultLang ) );
+ $out->addHTML( $this->doRenderLangOpt( $renderLangOptions, $renderLang ) );
}
// Add cannot animate thumbnail warning
* Output a drop-down box for language options for the file
*
* @param array $langChoices Array of string language codes
- * @param string $curLang Language code file is being viewed in.
- * @param string $defaultLang Language code that image is rendered in by default
+ * @param string $renderLang Language code for the language we want the file to rendered in.
* @return string HTML to insert underneath image.
*/
- protected function doRenderLangOpt( array $langChoices, $curLang, $defaultLang ) {
+ protected function doRenderLangOpt( array $langChoices, $renderLang ) {
global $wgScript;
- sort( $langChoices );
- $curLang = LanguageCode::bcp47( $curLang );
- $defaultLang = LanguageCode::bcp47( $defaultLang );
$opts = '';
- $haveCurrentLang = false;
- $haveDefaultLang = false;
-
- // We make a list of all the language choices in the file.
- // Additionally if the default language to render this file
- // is not included as being in this file (for example, in svgs
- // usually the fallback content is the english content) also
- // include a choice for that. Last of all, if we're viewing
- // the file in a language not on the list, add it as a choice.
+
+ $matchedRenderLang = $this->displayImg->getMatchedLanguage( $renderLang );
+
foreach ( $langChoices as $lang ) {
- $code = LanguageCode::bcp47( $lang );
- $name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
- if ( $name !== '' ) {
- $display = $this->getContext()->msg( 'img-lang-opt', $code, $name )->text();
- } else {
- $display = $code;
- }
- $opts .= "\n" . Xml::option( $display, $code, $curLang === $code );
- if ( $curLang === $code ) {
- $haveCurrentLang = true;
- }
- if ( $defaultLang === $code ) {
- $haveDefaultLang = true;
- }
- }
- if ( !$haveDefaultLang ) {
- // Its hard to know if the content is really in the default language, or
- // if its just unmarked content that could be in any language.
- $opts = Xml::option(
- $this->getContext()->msg( 'img-lang-default' )->text(),
- $defaultLang,
- $defaultLang === $curLang
- ) . $opts;
- }
- if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
- $name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
- if ( $name !== '' ) {
- $display = $this->getContext()->msg( 'img-lang-opt', $curLang, $name )->text();
- } else {
- $display = $curLang;
- }
- $opts = Xml::option( $display, $curLang, true ) . $opts;
+ $opts .= $this->createXmlOptionStringForLanguage(
+ $lang,
+ $matchedRenderLang === $lang
+ );
}
+ // Allow for the default case in an svg <switch> that is displayed if no
+ // systemLanguage attribute matches
+ $opts .= "\n" .
+ Xml::option(
+ $this->getContext()->msg( 'img-lang-default' )->text(),
+ 'und',
+ is_null( $matchedRenderLang )
+ );
+
$select = Html::rawElement(
'select',
[ 'id' => 'mw-imglangselector', 'name' => 'lang' ],
return $langSelectLine;
}
+ /**
+ * @param $lang string
+ * @param $selected bool
+ * @return string
+ */
+ private function createXmlOptionStringForLanguage( $lang, $selected ) {
+ $code = LanguageCode::bcp47( $lang );
+ $name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
+ if ( $name !== '' ) {
+ $display = $this->getContext()->msg( 'img-lang-opt', $code, $name )->text();
+ } else {
+ $display = $code;
+ }
+ return "\n" .
+ Xml::option(
+ $display,
+ $lang,
+ $selected
+ );
+ }
+
/**
* Get the width and height to display image at.
*
!! options
parsoid=wt2html,wt2wt,html2html
!! wikitext
-[[File:Foobar.svg|thumb|caption|lang=invalid.language.code]]
+[[File:Foobar.svg|thumb|caption|lang=invalid:language:code]]
!! html/php
-<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="135" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"></a></div>lang=invalid.language.code</div></div></div>
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="135" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"></a></div>lang=invalid:language:code</div></div></div>
!! html/parsoid
<figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="./File:Foobar.svg"><img resource="./File:Foobar.svg" src="//example.com/images/f/ff/Foobar.svg" data-file-width="240" data-file-height="180" data-file-type="drawing" height="165" width="220"/></a><figcaption>lang=invalid.language.code</figcaption></figure>
*/
class SvgTest extends MediaWikiMediaTestCase {
+ /**
+ * @var SvgHandler
+ */
+ private $handler;
+
protected function setUp() {
parent::setUp();
[ 'Wikimedia-logo.svg', [] ]
];
}
+
+ /**
+ * @param string $userPreferredLanguage
+ * @param array $svgLanguages
+ * @param string $expectedMatch
+ * @dataProvider providerGetMatchedLanguage
+ * @covers SvgHandler::getMatchedLanguage
+ */
+ public function testGetMatchedLanguage( $userPreferredLanguage, $svgLanguages, $expectedMatch ) {
+ $match = $this->handler->getMatchedLanguage( $userPreferredLanguage, $svgLanguages );
+ $this->assertEquals( $expectedMatch, $match );
+ }
+
+ public function providerGetMatchedLanguage() {
+ return [
+ 'no match' => [
+ 'userPreferredLanguage' => 'en',
+ 'svgLanguages' => [ 'de-DE', 'zh', 'ga', 'fr', 'sr-Latn-ME' ],
+ 'expectedMatch' => null,
+ ],
+ 'no subtags' => [
+ 'userPreferredLanguage' => 'en',
+ 'svgLanguages' => [ 'de', 'zh', 'en', 'fr' ],
+ 'expectedMatch' => 'en',
+ ],
+ 'user no subtags, svg 1 subtag' => [
+ 'userPreferredLanguage' => 'en',
+ 'svgLanguages' => [ 'de-DE', 'en-GB', 'en-US', 'fr' ],
+ 'expectedMatch' => 'en-GB',
+ ],
+ 'user no subtags, svg >1 subtag' => [
+ 'userPreferredLanguage' => 'sr',
+ 'svgLanguages' => [ 'de-DE', 'sr-Cyrl-BA', 'sr-Latn-ME', 'en-US', 'fr' ],
+ 'expectedMatch' => 'sr-Cyrl-BA',
+ ],
+ 'user 1 subtag, svg no subtags' => [
+ 'userPreferredLanguage' => 'en-US',
+ 'svgLanguages' => [ 'de', 'en', 'en', 'fr' ],
+ 'expectedMatch' => null,
+ ],
+ 'user 1 subtag, svg 1 subtag' => [
+ 'userPreferredLanguage' => 'en-US',
+ 'svgLanguages' => [ 'de-DE', 'en-GB', 'en-US', 'fr' ],
+ 'expectedMatch' => 'en-US',
+ ],
+ 'user 1 subtag, svg >1 subtag' => [
+ 'userPreferredLanguage' => 'sr-Latn',
+ 'svgLanguages' => [ 'de-DE', 'sr-Cyrl-BA', 'sr-Latn-ME', 'fr' ],
+ 'expectedMatch' => 'sr-Latn-ME',
+ ],
+ 'user >1 subtag, svg >1 subtag' => [
+ 'userPreferredLanguage' => 'sr-Latn-ME',
+ 'svgLanguages' => [ 'de-DE', 'sr-Cyrl-BA', 'sr-Latn-ME', 'en-US', 'fr' ],
+ 'expectedMatch' => 'sr-Latn-ME',
+ ],
+ 'user >1 subtag, svg <=1 subtag' => [
+ 'userPreferredLanguage' => 'sr-Latn-ME',
+ 'svgLanguages' => [ 'de-DE', 'sr-Cyrl', 'sr-Latn', 'en-US', 'fr' ],
+ 'expectedMatch' => null,
+ ],
+ 'ensure case-insensitive' => [
+ 'userPreferredLanguage' => 'sr-latn',
+ 'svgLanguages' => [ 'de-DE', 'sr-Cyrl', 'sr-Latn-ME', 'en-US', 'fr' ],
+ 'expectedMatch' => 'sr-Latn-ME',
+ ],
+ ];
+ }
}