*/
use CLDRPluralRuleParser\Evaluator;
-use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\MediaWikiServices;
+use Wikimedia\Assert\Assert;
/**
* Internationalisation code
/**
* Return autonyms in fetchLanguageName(s).
* @since 1.32
- * @deprecated since 1.34, LanguageNameUtils::AUTONYMS
*/
- const AS_AUTONYMS = LanguageNameUtils::AUTONYMS;
+ const AS_AUTONYMS = null;
/**
* Return all known languages in fetchLanguageName(s).
* @since 1.32
- * @deprecated since 1.34, use LanguageNameUtils::ALL
*/
- const ALL = LanguageNameUtils::ALL;
+ const ALL = 'all';
/**
* Return in fetchLanguageName(s) only the languages for which we have at
* least some localisation.
* @since 1.32
- * @deprecated since 1.34, use LanguageNameUtils::SUPPORTED
*/
- const SUPPORTED = LanguageNameUtils::SUPPORTED;
+ const SUPPORTED = 'mwfile';
/**
- * @var LanguageConverter
+ * @var LanguageConverter|FakeConverter
*/
public $mConverter;
public $mVariants, $mCode, $mLoaded = false;
public $mMagicExtensions = [];
- private $mHtmlCode = null, $mParentLanguage = false;
+ private $mHtmlCode = null;
+ /** @var Language|false */
+ private $mParentLanguage = false;
public $dateFormatStrings = [];
public $mExtendedSpecialPageAliases;
/** @var LocalisationCache */
private $localisationCache;
- /** @var LanguageNameUtils */
- private $langNameUtils;
-
public static $mLangObjCache = [];
/**
*/
const STRICT_FALLBACKS = 1;
- // TODO Make these const once we drop HHVM support (T192166)
public static $mWeekdayMsgs = [
'sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
'friday', 'saturday'
*/
private static $grammarTransformations;
+ /**
+ * Cache for language names
+ * @var HashBagOStuff|null
+ */
+ private static $languageNameCache;
+
/**
* Unicode directional formatting characters, for embedBidi()
*/
* @return Language
*/
protected static function newFromCode( $code, $fallback = false ) {
- $langNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils();
- if ( !$langNameUtils->isValidCode( $code ) ) {
+ if ( !self::isValidCode( $code ) ) {
throw new MWException( "Invalid language code \"$code\"" );
}
- if ( !$langNameUtils->isValidBuiltInCode( $code ) ) {
+ if ( !self::isValidBuiltInCode( $code ) ) {
// It's not possible to customise this code with class files, so
// just return a Language object. This is to support uselang= hacks.
$lang = new Language;
// Keep trying the fallback list until we find an existing class
$fallbacks = self::getFallbacksFor( $code );
foreach ( $fallbacks as $fallbackCode ) {
- if ( !$langNameUtils->isValidBuiltInCode( $fallbackCode ) ) {
+ if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
}
if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MEDIAWIKI_INSTALL' ) ) {
throw new MWException( __METHOD__ . ' must not be used outside tests/installer' );
}
- if ( !defined( 'MEDIAWIKI_INSTALL' ) ) {
+ if ( defined( 'MW_PHPUNIT_TEST' ) ) {
MediaWikiServices::getInstance()->resetServiceForTesting( 'LocalisationCache' );
- MediaWikiServices::getInstance()->resetServiceForTesting( 'LanguageNameUtils' );
}
self::$mLangObjCache = [];
self::$fallbackLanguageCache = [];
self::$grammarTransformations = null;
+ self::$languageNameCache = null;
}
/**
* Checks whether any localisation is available for that language tag
* in MediaWiki (MessagesXx.php exists).
*
- * @deprecated since 1.34, use LanguageNameUtils
* @param string $code Language tag (in lower case)
* @return bool Whether language is supported
* @since 1.21
*/
public static function isSupportedLanguage( $code ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->isSupportedLanguage( $code );
+ if ( !self::isValidBuiltInCode( $code ) ) {
+ return false;
+ }
+
+ if ( $code === 'qqq' ) {
+ return false;
+ }
+
+ return is_readable( self::getMessagesFileName( $code ) ) ||
+ is_readable( self::getJsonMessagesFileName( $code ) );
}
/**
* not it exists. This includes codes which are used solely for
* customisation via the MediaWiki namespace.
*
- * @deprecated since 1.34, use LanguageNameUtils
- *
* @param string $code
*
* @return bool
*/
public static function isValidCode( $code ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()->isValidCode( $code );
+ static $cache = [];
+ Assert::parameterType( 'string', $code, '$code' );
+ if ( !isset( $cache[$code] ) ) {
+ // People think language codes are html safe, so enforce it.
+ // Ideally we should only allow a-zA-Z0-9-
+ // but, .+ and other chars are often used for {{int:}} hacks
+ // see bugs T39564, T39587, T38938
+ $cache[$code] =
+ // Protect against path traversal
+ strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
+ && !preg_match( MediaWikiTitleCodec::getTitleInvalidRegex(), $code );
+ }
+ return $cache[$code];
}
/**
* Returns true if a language code is of a valid form for the purposes of
* internal customisation of MediaWiki, via Messages*.php or *.json.
*
- * @deprecated since 1.34, use LanguageNameUtils
- *
* @param string $code
*
* @since 1.18
* @return bool
*/
public static function isValidBuiltInCode( $code ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->isValidBuiltInCode( $code );
+ Assert::parameterType( 'string', $code, '$code' );
+
+ return (bool)preg_match( '/^[a-z0-9-]{2,}$/', $code );
}
/**
* Returns true if a language code is an IETF tag known to MediaWiki.
*
- * @deprecated since 1.34, use LanguageNameUtils
- *
* @param string $tag
*
* @since 1.21
* @return bool
*/
public static function isKnownLanguageTag( $tag ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->isKnownLanguageTag( $tag );
+ // Quick escape for invalid input to avoid exceptions down the line
+ // when code tries to process tags which are not valid at all.
+ if ( !self::isValidBuiltInCode( $tag ) ) {
+ return false;
+ }
+
+ if ( isset( MediaWiki\Languages\Data\Names::$names[$tag] )
+ || self::fetchLanguageName( $tag, $tag ) !== ''
+ ) {
+ return true;
+ }
+
+ return false;
}
/**
} else {
$this->mCode = str_replace( '_', '-', strtolower( substr( static::class, 8 ) ) );
}
- $services = MediaWikiServices::getInstance();
- $this->localisationCache = $services->getLocalisationCache();
- $this->langNameUtils = $services->getLanguageNameUtils();
+ $this->localisationCache = MediaWikiServices::getInstance()->getLocalisationCache();
}
/**
* Reduce memory usage
+ * @suppress PhanTypeSuspiciousNonTraversableForeach
*/
function __destruct() {
foreach ( $this as $name => $value ) {
if ( $usemsg && wfMessage( $msg )->exists() ) {
return $this->getMessageFromDB( $msg );
}
- $name = $this->langNameUtils->getLanguageName( $code );
+ $name = self::fetchLanguageName( $code );
if ( $name ) {
return $name; # if it's defined as a language name, show that
} else {
/**
* Get an array of language names, indexed by code.
- *
- * @deprecated since 1.34, use LanguageNameUtils::getLanguageNames
* @param null|string $inLanguage Code of language in which to return the names
* Use self::AS_AUTONYMS for autonyms (native names)
* @param string $include One of:
* @since 1.20
*/
public static function fetchLanguageNames( $inLanguage = self::AS_AUTONYMS, $include = 'mw' ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->getLanguageNames( $inLanguage, $include );
+ $cacheKey = $inLanguage === self::AS_AUTONYMS ? 'null' : $inLanguage;
+ $cacheKey .= ":$include";
+ if ( self::$languageNameCache === null ) {
+ self::$languageNameCache = new HashBagOStuff( [ 'maxKeys' => 20 ] );
+ }
+
+ $ret = self::$languageNameCache->get( $cacheKey );
+ if ( !$ret ) {
+ $ret = self::fetchLanguageNamesUncached( $inLanguage, $include );
+ self::$languageNameCache->set( $cacheKey, $ret );
+ }
+ return $ret;
+ }
+
+ /**
+ * Uncached helper for fetchLanguageNames
+ * @param null|string $inLanguage Code of language in which to return the names
+ * Use self::AS_AUTONYMS for autonyms (native names)
+ * @param string $include One of:
+ * self::ALL all available languages
+ * 'mw' only if the language is defined in MediaWiki or wgExtraLanguageNames (default)
+ * self::SUPPORTED only if the language is in 'mw' *and* has a message file
+ * @return array Language code => language name (sorted by key)
+ */
+ private static function fetchLanguageNamesUncached(
+ $inLanguage = self::AS_AUTONYMS,
+ $include = 'mw'
+ ) {
+ global $wgExtraLanguageNames, $wgUsePigLatinVariant;
+
+ // If passed an invalid language code to use, fallback to en
+ if ( $inLanguage !== self::AS_AUTONYMS && !self::isValidCode( $inLanguage ) ) {
+ $inLanguage = 'en';
+ }
+
+ $names = [];
+
+ if ( $inLanguage ) {
+ # TODO: also include when $inLanguage is null, when this code is more efficient
+ Hooks::run( 'LanguageGetTranslatedLanguageNames', [ &$names, $inLanguage ] );
+ }
+
+ $mwNames = $wgExtraLanguageNames + MediaWiki\Languages\Data\Names::$names;
+ if ( $wgUsePigLatinVariant ) {
+ // Pig Latin (for variant development)
+ $mwNames['en-x-piglatin'] = 'Igpay Atinlay';
+ }
+
+ foreach ( $mwNames as $mwCode => $mwName ) {
+ # - Prefer own MediaWiki native name when not using the hook
+ # - For other names just add if not added through the hook
+ if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
+ $names[$mwCode] = $mwName;
+ }
+ }
+
+ if ( $include === self::ALL ) {
+ ksort( $names );
+ return $names;
+ }
+
+ $returnMw = [];
+ $coreCodes = array_keys( $mwNames );
+ foreach ( $coreCodes as $coreCode ) {
+ $returnMw[$coreCode] = $names[$coreCode];
+ }
+
+ if ( $include === self::SUPPORTED ) {
+ $namesMwFile = [];
+ # We do this using a foreach over the codes instead of a directory
+ # loop so that messages files in extensions will work correctly.
+ foreach ( $returnMw as $code => $value ) {
+ if ( is_readable( self::getMessagesFileName( $code ) )
+ || is_readable( self::getJsonMessagesFileName( $code ) )
+ ) {
+ $namesMwFile[$code] = $names[$code];
+ }
+ }
+
+ ksort( $namesMwFile );
+ return $namesMwFile;
+ }
+
+ ksort( $returnMw );
+ # 'mw' option; default if it's not one of the other two options (all/mwfile)
+ return $returnMw;
}
/**
- * @deprecated since 1.34, use LanguageNameUtils::getLanguageName
* @param string $code The code of the language for which to get the name
* @param null|string $inLanguage Code of language in which to return the name
* (SELF::AS_AUTONYMS for autonyms)
$inLanguage = self::AS_AUTONYMS,
$include = self::ALL
) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->getLanguageName( $code, $inLanguage, $include );
+ $code = strtolower( $code );
+ $array = self::fetchLanguageNames( $inLanguage, $include );
+ return !array_key_exists( $code, $array ) ? '' : $array[$code];
}
/**
/**
* Get the name of a file for a certain language code
- *
- * @deprecated since 1.34, use LanguageNameUtils
* @param string $prefix Prepend this to the filename
* @param string $code Language code
* @param string $suffix Append this to the filename
* @return string $prefix . $mangledCode . $suffix
*/
public static function getFileName( $prefix, $code, $suffix = '.php' ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->getFileName( $prefix, $code, $suffix );
+ if ( !self::isValidBuiltInCode( $code ) ) {
+ throw new MWException( "Invalid language code \"$code\"" );
+ }
+
+ return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
}
/**
- * @deprecated since 1.34, use LanguageNameUtils
* @param string $code
* @return string
*/
public static function getMessagesFileName( $code ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->getMessagesFileName( $code );
+ global $IP;
+ $file = self::getFileName( "$IP/languages/messages/Messages", $code, '.php' );
+ Hooks::run( 'Language::getMessagesFileName', [ $code, &$file ] );
+ return $file;
}
/**
- * @deprecated since 1.34, use LanguageNameUtils
* @param string $code
* @return string
* @throws MWException
* @since 1.23
*/
public static function getJsonMessagesFileName( $code ) {
- return MediaWikiServices::getInstance()->getLanguageNameUtils()
- ->getJsonMessagesFileName( $code );
+ global $IP;
+
+ if ( !self::isValidBuiltInCode( $code ) ) {
+ throw new MWException( "Invalid language code \"$code\"" );
+ }
+
+ return "$IP/languages/i18n/$code.json";
}
/**