Add Message::listParam()

This allows for passing a list of values that will be turned into a list
in the context of the language for which the Message is being processed.

For example, currently you'd have to do

 $msg = new Message( 'something', [ $language->commaList( $list ) ] );

which isn't going to give correct results if the message is later
changed to a different language with a different value for
'comma-separator'.

Now, you can do this instead

 $msg = new Message( 'something', [ Message::listParam( $list, 'comma' ) ] );

and it will be listified properly no matter what language is later used to
parse $msg.

Change-Id: I66868c61832260870449998fef14c842f17753ee
This commit is contained in:
Brad Jorsch 2016-11-02 12:53:19 -04:00 committed by Anomie
parent 5a1104444f
commit 3041b5c038
3 changed files with 180 additions and 7 deletions

View file

@ -168,6 +168,17 @@ class Message implements MessageSpecifier, Serializable {
/** Transform {{..}} constructs, HTML-escape the result */
const FORMAT_ESCAPED = 'escaped';
/**
* Mapping from Message::listParam() types to Language methods.
* @var array
*/
protected static $listTypeMap = [
'comma' => 'commaList',
'semicolon' => 'semicolonList',
'pipe' => 'pipeList',
'text' => 'listToText',
];
/**
* In which language to get this message. True, which is the default,
* means the current user language, false content language.
@ -1070,6 +1081,22 @@ class Message implements MessageSpecifier, Serializable {
return [ 'plaintext' => $plaintext ];
}
/**
* @since 1.29
*
* @param array $list
* @param string $type 'comma', 'semicolon', 'pipe', 'text'
* @return array Array with "list" and "type" keys.
*/
public static function listParam( array $list, $type = 'text' ) {
if ( !isset( self::$listTypeMap[$type] ) ) {
throw new InvalidArgumentException(
"Invalid type '$type'. Known types are: " . join( ', ', array_keys( self::$listTypeMap ) )
);
}
return [ 'list' => $list, 'type' => $type ];
}
/**
* Substitutes any parameters into the message text.
*
@ -1123,6 +1150,8 @@ class Message implements MessageSpecifier, Serializable {
return [ 'before', $this->getLanguage()->formatBitrate( $param['bitrate'] ) ];
} elseif ( isset( $param['plaintext'] ) ) {
return [ 'after', $this->formatPlaintext( $param['plaintext'], $format ) ];
} elseif ( isset( $param['list'] ) ) {
return $this->formatListParam( $param['list'], $param['type'], $format );
} else {
$warning = 'Invalid parameter for message "' . $this->getKey() . '": ' .
htmlspecialchars( serialize( $param ) );
@ -1251,6 +1280,54 @@ class Message implements MessageSpecifier, Serializable {
}
}
/**
* Formats a list of parameters as a concatenated string.
* @since 1.29
* @param array $params
* @param string $listType
* @param string $format One of the FORMAT_* constants.
* @return array Array with the parameter type (either "before" or "after") and the value.
*/
protected function formatListParam( array $params, $listType, $format ) {
if ( !isset( self::$listTypeMap[$listType] ) ) {
$warning = 'Invalid list type for message "' . $this->getKey() . '": ' .
htmlspecialchars( serialize( $param ) );
trigger_error( $warning, E_USER_WARNING );
$e = new Exception;
wfDebugLog( 'Bug58676', $warning . "\n" . $e->getTraceAsString() );
return [ 'before', '[INVALID]' ];
}
$func = self::$listTypeMap[$listType];
// Handle an empty list sensibly
if ( !$params ) {
return [ 'before', $this->getLanguage()->$func( [] ) ];
}
// First, determine what kinds of list items we have
$types = [];
$vars = [];
$list = [];
foreach ( $params as $n => $p ) {
list( $type, $value ) = $this->extractParam( $p, $format );
$types[$type] = true;
$list[] = $value;
$vars[] = '$' . ( $n + 1 );
}
// Easy case: all are 'before' or 'after', so just join the
// values and use the same type.
if ( count( $types ) === 1 ) {
return [ key( $types ), $this->getLanguage()->$func( $list ) ];
}
// Hard case: We need to process each value per its type, then
// return the concatenated values as 'after'. We handle this by turning
// the list into a RawMessage and processing that as a parameter.
$vars = $this->getLanguage()->$func( $vars );
return $this->extractParam( new RawMessage( $vars, $params ), $format );
}
}
/**

View file

@ -1412,13 +1412,7 @@ class ApiPageSet extends ApiBase {
ApiBase::PARAM_DFLT => false,
ApiBase::PARAM_HELP_MSG => [
'api-pageset-param-converttitles',
new DeferredStringifier(
function ( IContextSource $context ) {
return $context->getLanguage()
->commaList( LanguageConverter::$languagesWithVariants );
},
$this
)
[ Message::listParam( LanguageConverter::$languagesWithVariants, 'text' ) ],
],
],
];

View file

@ -512,6 +512,108 @@ class MessageTest extends MediaWikiLangTestCase {
);
}
public static function provideListParam() {
$lang = Language::factory( 'de' );
$msg1 = new Message( 'mainpage', [], $lang );
$msg2 = new RawMessage( "''link''", [], $lang );
return [
'Simple comma list' => [
[ 'a', 'b', 'c' ],
'comma',
'text',
'a, b, c'
],
'Simple semicolon list' => [
[ 'a', 'b', 'c' ],
'semicolon',
'text',
'a; b; c'
],
'Simple pipe list' => [
[ 'a', 'b', 'c' ],
'pipe',
'text',
'a | b | c'
],
'Simple text list' => [
[ 'a', 'b', 'c' ],
'text',
'text',
'a, b and c'
],
'Empty list' => [
[],
'comma',
'text',
''
],
'List with all "before" params, ->text()' => [
[ "''link''", Message::numParam( 12345678 ) ],
'semicolon',
'text',
'\'\'link\'\'; 12,345,678'
],
'List with all "before" params, ->parse()' => [
[ "''link''", Message::numParam( 12345678 ) ],
'semicolon',
'parse',
'<i>link</i>; 12,345,678'
],
'List with all "after" params, ->text()' => [
[ $msg1, $msg2, Message::rawParam( '[[foo]]' ) ],
'semicolon',
'text',
'Main Page; \'\'link\'\'; [[foo]]'
],
'List with all "after" params, ->parse()' => [
[ $msg1, $msg2, Message::rawParam( '[[foo]]' ) ],
'semicolon',
'parse',
'Main Page; <i>link</i>; [[foo]]'
],
'List with both "before" and "after" params, ->text()' => [
[ $msg1, $msg2, Message::rawParam( '[[foo]]' ), "''link''", Message::numParam( 12345678 ) ],
'semicolon',
'text',
'Main Page; \'\'link\'\'; [[foo]]; \'\'link\'\'; 12,345,678'
],
'List with both "before" and "after" params, ->parse()' => [
[ $msg1, $msg2, Message::rawParam( '[[foo]]' ), "''link''", Message::numParam( 12345678 ) ],
'semicolon',
'parse',
'Main Page; <i>link</i>; [[foo]]; <i>link</i>; 12,345,678'
],
];
}
/**
* @covers Message::listParam
* @covers Message::extractParam
* @covers Message::formatListParam
* @dataProvider provideListParam
*/
public function testListParam( $list, $type, $format, $expect ) {
$lang = Language::factory( 'en' );
$msg = new RawMessage( '$1' );
$msg->params( [ Message::listParam( $list, $type ) ] );
$this->assertEquals(
$expect,
$msg->inLanguage( $lang )->$format()
);
}
/**
* @covers Message::extractParam
*/