* @ingroup Language
*/
class Language {
+ /**
+ * Return autonyms in fetchLanguageName(s).
+ * @since 1.32
+ */
+ const AS_AUTONYMS = null;
+
+ /**
+ * Return all known languages in fetchLanguageName(s).
+ * @since 1.32
+ */
+ const ALL = 'all';
+
+ /**
+ * Return in fetchLanguageName(s) only the languages for which we have at
+ * least some localisation.
+ * @since 1.32
+ */
+ const SUPPORTED = 'mwfile';
+
/**
* @var LanguageConverter
*/
// Check if there is a language class for the code
$class = self::classFromCode( $code, $fallback );
- if ( class_exists( $class ) ) {
+ // LanguageCode does not inherit Language
+ if ( class_exists( $class ) && is_a( $class, 'Language', true ) ) {
$lang = new $class;
return $lang;
}
throw new MWException( "Invalid fallback sequence for language '$code'" );
}
+ /**
+ * Intended for tests that may change configuration in a way that invalidates caches.
+ *
+ * @since 1.32
+ */
+ public static function clearCaches() {
+ if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+ throw new MWException( __METHOD__ . ' must not be used outside tests' );
+ }
+ self::$dataCache = null;
+ // Reinitialize $dataCache, since it's expected to always be available
+ self::getLocalisationCache();
+ 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).
* Get a namespace value by key
*
* <code>
- * $mw_ns = $wgContLang->getNsText( NS_MEDIAWIKI );
+ * $mw_ns = $lang->getNsText( NS_MEDIAWIKI );
* echo $mw_ns; // prints 'MediaWiki'
* </code>
*
* producing output.
*
* <code>
- * $mw_ns = $wgContLang->getFormattedNsText( NS_MEDIAWIKI_TALK );
+ * $mw_ns = $lang->getFormattedNsText( NS_MEDIAWIKI_TALK );
* echo $mw_ns; // prints 'MediaWiki talk'
* </code>
*
/**
* Get an array of language names, indexed by code.
* @param null|string $inLanguage Code of language in which to return the names
- * Use null for autonyms (native names)
+ * Use self::AS_AUTONYMS for autonyms (native names)
* @param string $include One of:
- * 'all' all available languages
+ * self::ALL all available languages
* 'mw' only if the language is defined in MediaWiki or wgExtraLanguageNames (default)
- * 'mwfile' only if the language is in 'mw' *and* has a message file
+ * self::SUPPORTED only if the language is in 'mw' *and* has a message file
* @return array Language code => language name (sorted by key)
* @since 1.20
*/
- public static function fetchLanguageNames( $inLanguage = null, $include = 'mw' ) {
- $cacheKey = $inLanguage === null ? 'null' : $inLanguage;
+ public static function fetchLanguageNames( $inLanguage = self::AS_AUTONYMS, $include = 'mw' ) {
+ $cacheKey = $inLanguage === self::AS_AUTONYMS ? 'null' : $inLanguage;
$cacheKey .= ":$include";
if ( self::$languageNameCache === null ) {
self::$languageNameCache = new HashBagOStuff( [ 'maxKeys' => 20 ] );
/**
* Uncached helper for fetchLanguageNames
* @param null|string $inLanguage Code of language in which to return the names
- * Use null for autonyms (native names)
+ * Use self::AS_AUTONYMS for autonyms (native names)
* @param string $include One of:
- * 'all' all available languages
+ * self::ALL all available languages
* 'mw' only if the language is defined in MediaWiki or wgExtraLanguageNames (default)
- * 'mwfile' only if the language is in 'mw' *and* has a message file
+ * 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 = null, $include = 'mw' ) {
+ 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 !== null && !self::isValidCode( $inLanguage ) ) {
+ if ( $inLanguage !== self::AS_AUTONYMS && !self::isValidCode( $inLanguage ) ) {
$inLanguage = 'en';
}
}
}
- if ( $include === 'all' ) {
+ if ( $include === self::ALL ) {
ksort( $names );
return $names;
}
$returnMw[$coreCode] = $names[$coreCode];
}
- if ( $include === 'mwfile' ) {
+ 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.
/**
* @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 (null for autonyms)
- * @param string $include 'all', 'mw' or 'mwfile'; see fetchLanguageNames()
+ * @param null|string $inLanguage Code of language in which to return the name
+ * (SELF::AS_AUTONYMS for autonyms)
+ * @param string $include See fetchLanguageNames()
* @return string Language name or empty
* @since 1.20
*/
- public static function fetchLanguageName( $code, $inLanguage = null, $include = 'all' ) {
+ public static function fetchLanguageName(
+ $code,
+ $inLanguage = self::AS_AUTONYMS,
+ $include = self::ALL
+ ) {
$code = strtolower( $code );
$array = self::fetchLanguageNames( $inLanguage, $include );
return !array_key_exists( $code, $array ) ? '' : $array[$code];
* @param string $ts 14-character timestamp
* YYYYMMDDHHMMSS
* 01234567890123
- * @param DateTimeZone $zone Timezone of $ts
+ * @param DateTimeZone|null $zone Timezone of $ts
* @param int &$ttl The amount of time (in seconds) the output may be cached for.
* Only makes sense if $ts is the current time.
* @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai?
# Add 543 years to the Gregorian calendar
# Months and days are identical
$gy_offset = $gy + 543;
+ # fix for dates between 1912 and 1941
+ # https://en.wikipedia.org/?oldid=836596673#New_year
+ if ( $gy >= 1912 && $gy <= 1940 ) {
+ if ( $gm <= 3 ) {
+ $gy_offset--;
+ }
+ $gm = ( $gm - 3 ) % 12;
+ }
} elseif ( ( !strcmp( $cName, 'minguo' ) ) || !strcmp( $cName, 'juche' ) ) {
# Minguo dates
# Deduct 1911 years from the Gregorian calendar
*
* @return string
*/
- function normalize( $s ) {
+ public function normalize( $s ) {
global $wgAllUnicodeFixes;
$s = UtfNormal\Validator::cleanUp( $s );
if ( $wgAllUnicodeFixes ) {
* Take a list of strings and build a locale-friendly comma-separated
* list, using the local comma-separator message.
* The last two strings are chained with an "and".
- * NOTE: This function will only work with standard numeric array keys (0, 1, 2…)
*
- * @param string[] $l
+ * @param string[] $list
* @return string
*/
- function listToText( array $l ) {
- $m = count( $l ) - 1;
- if ( $m < 0 ) {
+ public function listToText( array $list ) {
+ $itemCount = count( $list );
+ if ( $itemCount < 1 ) {
return '';
}
- if ( $m > 0 ) {
+ $text = array_pop( $list );
+ if ( $itemCount > 1 ) {
$and = $this->msg( 'and' )->escaped();
$space = $this->msg( 'word-separator' )->escaped();
- if ( $m > 1 ) {
+ $comma = '';
+ if ( $itemCount > 2 ) {
$comma = $this->msg( 'comma-separator' )->escaped();
}
+ $text = implode( $comma, $list ) . $and . $space . $text;
}
- $s = $l[$m];
- for ( $i = $m - 1; $i >= 0; $i-- ) {
- if ( $i == $m - 1 ) {
- $s = $l[$i] . $and . $space . $s;
- } else {
- $s = $l[$i] . $comma . $s;
- }
- }
- return $s;
+ return $text;
}
/**
* @return string
*/
function truncate( $string, $length, $ellipsis = '...', $adjustLength = true ) {
+ wfDeprecated( __METHOD__, '1.31' );
return $this->truncateForDatabase( $string, $length, $ellipsis, $adjustLength );
}
* match up with it.
*
* @param string $str The validated block duration in English
- * @param User $user User object to use timezone from or null for $wgUser
+ * @param User|null $user User object to use timezone from or null for $wgUser
* @param int $now Current timestamp, for formatting relative block durations
* @return string Somehow translated block duration
* @see LanguageFi.php for example implementation
/**
* convert text to different variants of a language.
*
- * @param string $text
- * @return string
+ * @warning Glossary state is maintained between calls. This means
+ * if you pass unescaped text to this method it can cause an XSS
+ * in later calls to this method, even if the later calls have properly
+ * escaped the input. Never feed this method user controlled text that
+ * is not properly escaped!
+ * @param string $text Content that has been already escaped for use in HTML
+ * @return string HTML
*/
public function convert( $text ) {
return $this->mConverter->convert( $text );
* @return bool
*/
public function equals( Language $lang ) {
- return $lang->getCode() === $this->mCode;
+ return $lang === $this || $lang->getCode() === $this->mCode;
}
/**