162 const FORMAT_PLAIN =
'plain';
164 const FORMAT_BLOCK_PARSE =
'block-parse';
166 const FORMAT_PARSE =
'parse';
168 const FORMAT_TEXT =
'text';
170 const FORMAT_ESCAPED =
'escaped';
176 protected static $listTypeMap = [
177 'comma' =>
'commaList',
178 'semicolon' =>
'semicolonList',
179 'pipe' =>
'pipeList',
180 'text' =>
'listToText',
189 protected $interface =
true;
196 protected $language =
false;
207 protected $keysToTry;
212 protected $parameters = [];
218 protected $format =
'parse';
223 protected $useDatabase =
true;
249 public function __construct( $key,
$params = [],
Language $language =
null ) {
252 throw new InvalidArgumentException(
253 '$params must be empty if $key is a MessageSpecifier'
257 $key = $key->getKey();
260 if ( !is_string( $key ) && !is_array( $key ) ) {
261 throw new InvalidArgumentException(
'$key must be a string or an array' );
264 $this->keysToTry = (
array)$key;
266 if ( empty( $this->keysToTry ) ) {
267 throw new InvalidArgumentException(
'$key must not be an empty list' );
270 $this->
key = reset( $this->keysToTry );
272 $this->parameters = array_values(
$params );
275 $this->
language = $language ?:
false;
285 'interface' => $this->interface,
288 'keysToTry' => $this->keysToTry,
289 'parameters' => $this->parameters,
290 'format' => $this->format,
291 'useDatabase' => $this->useDatabase,
303 $this->
interface = $data['interface'];
304 $this->
key = $data[
'key'];
305 $this->keysToTry = $data[
'keysToTry'];
306 $this->parameters = $data[
'parameters'];
307 $this->
format = $data[
'format'];
308 $this->useDatabase = $data[
'useDatabase'];
310 $this->
title = $data[
'title'];
319 public function isMultiKey() {
320 return count( $this->keysToTry ) > 1;
329 public function getKeysToTry() {
330 return $this->keysToTry;
344 public function getKey() {
356 return $this->parameters;
367 public function getFormat() {
369 return $this->format;
379 public function getLanguage() {
396 public static function newFromKey( $key ) {
399 return new self( $key,
$params );
415 public static function newFromSpecifier(
$value ) {
417 if ( is_array(
$value ) ) {
422 if (
$value instanceof Message ) {
425 $message =
new Message(
$value );
426 } elseif ( is_string(
$value ) ) {
429 throw new InvalidArgumentException( __METHOD__ .
': invalid argument type '
448 public static function newFallbackSequence( ) {
449 $keys = func_get_args();
450 if ( func_num_args() == 1 ) {
451 if ( is_array(
$keys[0] ) ) {
459 return new self(
$keys );
472 public function getTitle() {
475 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
476 $lang = $this->getLanguage();
479 !
$lang->equals( $contLang )
480 && in_array( $this->
key, (
array)$wgForceUIMsgAsContentMsg )
499 public function params( ) {
500 $args = func_get_args();
507 if (
$args[0] === [] ) {
511 if ( is_int( $key ) ) {
519 $this->parameters = array_merge( $this->parameters, array_values(
$args ) );
536 public function rawParams( ) {
542 $this->parameters[] = self::rawParam( $param );
558 public function numParams( ) {
564 $this->parameters[] = self::numParam( $param );
580 public function durationParams( ) {
586 $this->parameters[] = self::durationParam( $param );
602 public function expiryParams( ) {
608 $this->parameters[] = self::expiryParam( $param );
624 public function timeperiodParams( ) {
630 $this->parameters[] = self::timeperiodParam( $param );
646 public function sizeParams( ) {
652 $this->parameters[] = self::sizeParam( $param );
668 public function bitrateParams( ) {
674 $this->parameters[] = self::bitrateParam( $param );
692 public function plaintextParams( ) {
698 $this->parameters[] = self::plaintextParam( $param );
713 $this->inLanguage(
$context->getLanguage() );
715 $this->
interface =
true;
731 public function inLanguage(
$lang ) {
732 $previousLanguage = $this->language;
736 } elseif ( is_string(
$lang ) ) {
743 $type = gettype(
$lang );
745 .
"passed a String or Language object; $type given"
749 if ( $this->
language !== $previousLanguage ) {
753 $this->
interface =
false;
766 public function inContentLanguage() {
768 if ( in_array( $this->
key, (
array)$wgForceUIMsgAsContentMsg ) ) {
772 $this->inLanguage( MediaWikiServices::getInstance()->getContentLanguage() );
786 public function setInterfaceMessageFlag( $interface ) {
787 $this->
interface = (bool)$interface;
800 public function useDatabase( $useDatabase ) {
801 $this->useDatabase = (bool)$useDatabase;
844 public function toString( $format =
null ) {
845 if ( $format ===
null ) {
846 $ex =
new LogicException( __METHOD__ .
' using implicit format: ' . $this->
format );
848 $ex->getMessage(), [
'exception' => $ex,
'format' => $this->format,
'key' => $this->key ] );
849 $format = $this->format;
851 $string = $this->fetchMessage();
853 if ( $string ===
false ) {
861 return '⧼' . htmlspecialchars( $this->
key ) .
'⧽';
864 # Replace $* with a list of parameters for &uselang=qqx.
865 if ( strpos( $string,
'$*' ) !==
false ) {
867 if ( $this->parameters !== [] ) {
868 $paramlist =
': $' . implode(
', $', range( 1,
count( $this->parameters ) ) );
870 $string = str_replace(
'$*', $paramlist, $string );
873 # Replace parameters before text parsing
874 $string = $this->replaceParameters( $string,
'before', $format );
876 # Maybe transform using the full parser
877 if ( $format === self::FORMAT_PARSE ) {
878 $string = $this->parseText( $string );
879 $string = Parser::stripOuterParagraph( $string );
880 } elseif ( $format === self::FORMAT_BLOCK_PARSE ) {
881 $string = $this->parseText( $string );
882 } elseif ( $format === self::FORMAT_TEXT ) {
883 $string = $this->transformText( $string );
884 } elseif ( $format === self::FORMAT_ESCAPED ) {
885 $string = $this->transformText( $string );
886 $string = htmlspecialchars( $string, ENT_QUOTES,
'UTF-8',
false );
889 # Raw parameter replacement
890 $string = $this->replaceParameters( $string,
'after', $format );
904 public function __toString() {
909 return $this->toString( self::FORMAT_PARSE );
910 }
catch ( Exception $ex ) {
912 trigger_error(
"Exception caught in " . __METHOD__ .
" (message " . $this->
key .
"): "
913 . $ex, E_USER_WARNING );
914 }
catch ( Exception $ex ) {
918 return '⧼' . htmlspecialchars( $this->
key ) .
'⧽';
929 public function parse() {
930 $this->
format = self::FORMAT_PARSE;
931 return $this->toString( self::FORMAT_PARSE );
941 public function text() {
942 $this->
format = self::FORMAT_TEXT;
943 return $this->toString( self::FORMAT_TEXT );
953 public function plain() {
954 $this->
format = self::FORMAT_PLAIN;
955 return $this->toString( self::FORMAT_PLAIN );
965 public function parseAsBlock() {
966 $this->
format = self::FORMAT_BLOCK_PARSE;
967 return $this->toString( self::FORMAT_BLOCK_PARSE );
978 public function escaped() {
979 $this->
format = self::FORMAT_ESCAPED;
980 return $this->toString( self::FORMAT_ESCAPED );
990 public function exists() {
991 return $this->fetchMessage() !==
false;
1002 public function isBlank() {
1003 $message = $this->fetchMessage();
1004 return $message ===
false || $message ===
'';
1014 public function isDisabled() {
1015 $message = $this->fetchMessage();
1016 return $message ===
false || $message ===
'' || $message ===
'-';
1026 public static function rawParam( $raw ) {
1027 return [
'raw' => $raw ];
1037 public static function numParam( $num ) {
1038 return [
'num' => $num ];
1048 public static function durationParam( $duration ) {
1049 return [
'duration' => $duration ];
1059 public static function expiryParam( $expiry ) {
1060 return [
'expiry' => $expiry ];
1070 public static function timeperiodParam( $period ) {
1071 return [
'period' => $period ];
1081 public static function sizeParam( $size ) {
1082 return [
'size' => $size ];
1092 public static function bitrateParam( $bitrate ) {
1093 return [
'bitrate' => $bitrate ];
1103 public static function plaintextParam( $plaintext ) {
1104 return [
'plaintext' => $plaintext ];
1114 public static function listParam(
array $list,
$type =
'text' ) {
1115 if ( !isset( self::$listTypeMap[
$type] ) ) {
1116 throw new InvalidArgumentException(
1117 "Invalid type '$type'. Known types are: " . implode(
', ', array_keys( self::$listTypeMap ) )
1120 return [
'list' => $list,
'type' =>
$type ];
1134 protected function replaceParameters( $message,
$type, $format ) {
1139 $marker = $format === self::FORMAT_ESCAPED ?
'$' :
'$\'"';
1140 $replacementKeys = [];
1141 foreach ( $this->parameters
as $n => $param ) {
1142 list( $paramType,
$value ) = $this->extractParam( $param, $format );
1143 if (
$type ===
'before' ) {
1144 if ( $paramType ===
'before' ) {
1145 $replacementKeys[
'$' . ( $n + 1 )] =
$value;
1152 $replacementKeys[
'$' . ( $n + 1 )] = $marker . ( $n + 1 );
1155 if ( $paramType ===
'after' ) {
1156 $replacementKeys[$marker . ( $n + 1 )] =
$value;
1160 $message = strtr( $message, $replacementKeys );
1174 protected function extractParam( $param, $format ) {
1175 if ( is_array( $param ) ) {
1176 if ( isset( $param[
'raw'] ) ) {
1177 return [
'after', $param[
'raw'] ];
1178 } elseif ( isset( $param[
'num'] ) ) {
1181 return [
'before', $this->getLanguage()->formatNum( $param[
'num'] ) ];
1182 } elseif ( isset( $param[
'duration'] ) ) {
1183 return [
'before', $this->getLanguage()->formatDuration( $param[
'duration'] ) ];
1184 } elseif ( isset( $param[
'expiry'] ) ) {
1185 return [
'before', $this->getLanguage()->formatExpiry( $param[
'expiry'] ) ];
1186 } elseif ( isset( $param[
'period'] ) ) {
1187 return [
'before', $this->getLanguage()->formatTimePeriod( $param[
'period'] ) ];
1188 } elseif ( isset( $param[
'size'] ) ) {
1189 return [
'before', $this->getLanguage()->formatSize( $param[
'size'] ) ];
1190 } elseif ( isset( $param[
'bitrate'] ) ) {
1191 return [
'before', $this->getLanguage()->formatBitrate( $param[
'bitrate'] ) ];
1192 } elseif ( isset( $param[
'plaintext'] ) ) {
1193 return [
'after', $this->formatPlaintext( $param[
'plaintext'], $format ) ];
1194 } elseif ( isset( $param[
'list'] ) ) {
1195 return $this->formatListParam( $param[
'list'], $param[
'type'], $format );
1197 if ( !is_scalar( $param ) ) {
1201 'Invalid parameter for message "{msgkey}": {param}',
1203 'exception' =>
new Exception,
1204 'msgkey' => $this->
getKey(),
1205 'param' => htmlspecialchars( $param ),
1209 return [
'before',
'[INVALID]' ];
1211 } elseif ( $param instanceof Message ) {
1213 $msg = clone $param;
1214 if ( $msg->language !== $this->language || $msg->useDatabase !== $this->useDatabase ) {
1216 $msg->message =
null;
1218 $msg->interface = $this->interface;
1219 $msg->language = $this->language;
1220 $msg->useDatabase = $this->useDatabase;
1224 if ( $format ===
'block-parse' ) {
1227 $msg->format = $format;
1232 return [
'after', $msg->toString( $format ) ];
1234 return [
'before', $param ];
1247 protected function parseText( $string ) {
1253 $this->getLanguage()
1258 'enableSectionEditLinks' =>
false,
1277 protected function transformText( $string ) {
1281 $this->getLanguage(),
1294 protected function fetchMessage() {
1295 if ( $this->
message ===
null ) {
1298 foreach ( $this->keysToTry
as $key ) {
1299 $message =
$cache->get( $key, $this->useDatabase, $this->getLanguage() );
1300 if ( $message !==
false && $message !==
'' ) {
1310 return $this->message;
1325 protected function formatPlaintext( $plaintext, $format ) {
1326 switch ( $format ) {
1327 case self::FORMAT_TEXT:
1328 case self::FORMAT_PLAIN:
1331 case self::FORMAT_PARSE:
1332 case self::FORMAT_BLOCK_PARSE:
1333 case self::FORMAT_ESCAPED:
1335 return htmlspecialchars( $plaintext, ENT_QUOTES );
1347 protected function formatListParam(
array $params, $listType, $format ) {
1348 if ( !isset( self::$listTypeMap[$listType] ) ) {
1349 $warning =
'Invalid list type for message "' . $this->
getKey() .
'": '
1350 . htmlspecialchars( $listType )
1352 trigger_error( $warning, E_USER_WARNING );
1354 wfDebugLog(
'Bug58676', $warning .
"\n" .
$e->getTraceAsString() );
1355 return [
'before',
'[INVALID]' ];
1357 $func = self::$listTypeMap[$listType];
1361 return [
'before', $this->getLanguage()->$func( [] ) ];
1370 $types[
$type] =
true;
1372 $vars[] =
'$' . ( $n + 1 );
1377 if (
count( $types ) === 1 ) {
1378 return [
key( $types ), $this->getLanguage()->$func( $list ) ];
1384 $vars = $this->getLanguage()->$func(
$vars );