MessageFormatterFactory

An injectable service interface for message formatting, somewhat
narrowed compared to Message.

Only the text format is implemented in this framework so far, with
getTextFormatter() returning a formatter that converts to the text
format. Other formatters could be added to MessageFormatterFactory.

Bug: T226598
Change-Id: Id053074c1dbcb692e8309fdca602f94a385bca0c
This commit is contained in:
Tim Starling 2019-07-15 20:24:38 +10:00
parent 3bb3c8b0ae
commit 09cd8eb080
16 changed files with 923 additions and 0 deletions

View file

@ -134,6 +134,7 @@ class AutoLoader {
'MediaWiki\\Edit\\' => __DIR__ . '/edit/',
'MediaWiki\\EditPage\\' => __DIR__ . '/editpage/',
'MediaWiki\\Linker\\' => __DIR__ . '/linker/',
'MediaWiki\\Message\\' => __DIR__ . '/Message',
'MediaWiki\\Permissions\\' => __DIR__ . '/Permissions/',
'MediaWiki\\Preferences\\' => __DIR__ . '/preferences/',
'MediaWiki\\Rest\\' => __DIR__ . '/Rest/',
@ -143,6 +144,7 @@ class AutoLoader {
'MediaWiki\\Sparql\\' => __DIR__ . '/sparql/',
'MediaWiki\\Storage\\' => __DIR__ . '/Storage/',
'MediaWiki\\Tidy\\' => __DIR__ . '/tidy/',
'Wikimedia\\Message\\' => __DIR__ . '/libs/Message/',
'Wikimedia\\ParamValidator\\' => __DIR__ . '/libs/ParamValidator/',
'Wikimedia\\Services\\' => __DIR__ . '/libs/services/',
];

View file

@ -18,6 +18,7 @@ use MediaWiki\Block\BlockManager;
use MediaWiki\Block\BlockRestrictionStore;
use MediaWiki\FileBackend\FSFile\TempFSFileFactory;
use MediaWiki\Http\HttpRequestFactory;
use Wikimedia\Message\IMessageFormatterFactory;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Preferences\PreferencesFactory;
@ -709,6 +710,14 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'MessageCache' );
}
/**
* @since 1.34
* @return IMessageFormatterFactory
*/
public function getMessageFormatterFactory() {
return $this->getService( 'MessageFormatterFactory' );
}
/**
* @since 1.28
* @return MimeAnalyzer

View file

@ -0,0 +1,29 @@
<?php
namespace MediaWiki\Message;
use Wikimedia\Message\IMessageFormatterFactory;
use Wikimedia\Message\ITextFormatter;
/**
* The MediaWiki-specific implementation of IMessageFormatterFactory
*/
class MessageFormatterFactory implements IMessageFormatterFactory {
private $textFormatters = [];
/**
* Required parameters may be added to this function without deprecation.
* External callers should use MediaWikiServices::getMessageFormatterFactory().
*
* @internal
*/
public function __construct() {
}
public function getTextFormatter( $langCode ): ITextFormatter {
if ( !isset( $this->textFormatters[$langCode] ) ) {
$this->textFormatters[$langCode] = new TextFormatter( $langCode );
}
return $this->textFormatters[$langCode];
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace MediaWiki\Message;
use Wikimedia\Message\ITextFormatter;
use Wikimedia\Message\ListParam;
use Wikimedia\Message\MessageParam;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Message;
/**
* The MediaWiki-specific implementation of ITextFormatter
*/
class TextFormatter implements ITextFormatter {
/** @var string */
private $langCode;
/**
* Construct a TextFormatter.
*
* The type signature may change without notice as dependencies are added
* to the constructor. External callers should use
* MediaWikiServices::getMessageFormatterFactory()
*
* @internal
*/
public function __construct( $langCode ) {
$this->langCode = $langCode;
}
/**
* Allow the Message class to be mocked in tests by constructing objects in
* a protected method.
*
* @internal
* @param string $key
* @return Message
*/
protected function createMessage( $key ) {
return new Message( $key );
}
public function getLangCode() {
return $this->langCode;
}
private static function convertParam( MessageParam $param ) {
if ( $param instanceof ListParam ) {
$convertedElements = [];
foreach ( $param->getValue() as $element ) {
$convertedElements[] = self::convertParam( $element );
}
return Message::listParam( $convertedElements, $param->getListType() );
} elseif ( $param instanceof MessageParam ) {
if ( $param->getType() === ParamType::TEXT ) {
return $param->getValue();
} else {
return [ $param->getType() => $param->getValue() ];
}
} else {
throw new \InvalidArgumentException( 'Invalid message parameter type' );
}
}
public function format( MessageValue $mv ) {
$message = $this->createMessage( $mv->getKey() );
foreach ( $mv->getParams() as $param ) {
$message->params( self::convertParam( $param ) );
}
$message->inLanguage( $this->langCode );
return $message->text();
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace MediaWiki\Rest;
use Wikimedia\Message\MessageValue;
class LocalizedHttpException extends HttpException {
public function __construct( MessageValue $message, $code = 500 ) {
parent::__construct( 'Localized exception with key ' . $message->getKey(), $code );
}
}

View file

@ -52,6 +52,8 @@ use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkRendererFactory;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use Wikimedia\Message\IMessageFormatterFactory;
use MediaWiki\Message\MessageFormatterFactory;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Preferences\PreferencesFactory;
@ -350,6 +352,11 @@ return [
);
},
'MessageFormatterFactory' =>
function ( MediaWikiServices $services ) : IMessageFormatterFactory {
return new MessageFormatterFactory();
},
'MimeAnalyzer' => function ( MediaWikiServices $services ) : MimeAnalyzer {
$logger = LoggerFactory::getInstance( 'Mime' );
$mainConfig = $services->getMainConfig();

View file

@ -0,0 +1,18 @@
<?php
namespace Wikimedia\Message;
/**
* A simple factory providing a message formatter for a given language code.
*
* @see ITextFormatter
*/
interface IMessageFormatterFactory {
/**
* Get a text message formatter for a given language.
*
* @param string $langCode The language code
* @return ITextFormatter
*/
public function getTextFormatter( $langCode ): ITextFormatter;
}

View file

@ -0,0 +1,43 @@
<?php
namespace Wikimedia\Message;
/**
* ITextFormatter is a simplified interface to the Message class. It converts
* MessageValue message specifiers to localized text in a certain language.
*
* MessageValue supports message keys, and parameters with a wide variety of
* types. It does not expose any details of how messages are retrieved from
* storage or what format they are stored in.
*
* Thus, TextFormatter supports single message keys, but not the concept of
* presence or absence of a key from storage. So it does not support
* fallback sequences of multiple keys.
*
* The caller cannot modify the details of message translation, such as which
* of multiple sources the message is taken from. Any such flags may be injected
* into the factory constructor.
*
* Implementations of TextFormatter are not required to perfectly format
* any message in any language. Implementations should make a best effort to
* produce human-readable text.
*
* @package MediaWiki\MessageFormatter
*/
interface ITextFormatter {
/**
* Get the internal language code in which format() is
* @return string
*/
function getLangCode();
/**
* Convert a MessageValue to text.
*
* The result is not safe for use as raw HTML.
*
* @param MessageValue $message
* @return string
*/
function format( MessageValue $message );
}

View file

@ -0,0 +1,52 @@
<?php
namespace Wikimedia\Message;
/**
* The class for list parameters
*/
class ListParam extends MessageParam {
private $listType;
/**
* @param string $listType One of the ListType constants:
* - ListType::COMMA: A comma-separated list
* - ListType::SEMICOLON: A semicolon-separated list
* - ListType::PIPE: A pipe-separated list
* - ListType::TEXT: A natural language list, separated by commas and
* the word "and".
* @param (MessageParam|string)[] $elements An array of parameters
*/
public function __construct( $listType, array $elements ) {
$this->type = ParamType::LIST;
$this->listType = $listType;
$this->value = [];
foreach ( $elements as $element ) {
if ( $element instanceof MessageParam ) {
$this->value[] = $element;
} elseif ( is_scalar( $element ) ) {
$this->value[] = new TextParam( ParamType::TEXT, $element );
} else {
throw new \InvalidArgumentException(
'ListParam elements must be MessageParam or scalar' );
}
}
}
/**
* Get the type of the list
*
* @return string One of the ListType constants
*/
public function getListType() {
return $this->listType;
}
public function dump() {
$contents = '';
foreach ( $this->value as $element ) {
$contents .= $element->dump();
}
return "<{$this->type} listType=\"{$this->listType}\">$contents</{$this->type}>";
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Wikimedia\Message;
/**
* The constants used to specify list types. The values of the constants are an
* unstable implementation detail and correspond to the names of the list types
* in the Message class.
*/
class ListType {
/** A comma-separated list */
const COMMA = 'comma';
/** A semicolon-separated list */
const SEMICOLON = 'semicolon';
/** A pipe-separated list */
const PIPE = 'pipe';
/** A natural-language list separated by "and" */
const AND = 'text';
}

View file

@ -0,0 +1,36 @@
<?php
namespace Wikimedia\Message;
/**
* The base class for message parameters.
*/
abstract class MessageParam {
protected $type;
protected $value;
/**
* Get the type of the parameter.
*
* @return string One of the ParamType constants
*/
public function getType() {
return $this->type;
}
/**
* Get the input value of the parameter
*
* @return int|float|string|array
*/
public function getValue() {
return $this->value;
}
/**
* Dump the object for testing/debugging
*
* @return string
*/
abstract public function dump();
}

View file

@ -0,0 +1,258 @@
<?php
namespace Wikimedia\Message;
/**
* A MessageValue holds a key and an array of parameters
*/
class MessageValue {
/** @var string */
private $key;
/** @var MessageParam[] */
private $params;
/**
* @param string $key
* @param array $params Each element of the parameter array
* may be either a MessageParam or a scalar. If it is a scalar, it is
* converted to a parameter of type TEXT.
*/
public function __construct( $key, $params = [] ) {
$this->key = $key;
$this->params = [];
$this->params( ...$params );
}
/**
* Get the message key
*
* @return string
*/
public function getKey() {
return $this->key;
}
/**
* Get the parameter array
*
* @return MessageParam[]
*/
public function getParams() {
return $this->params;
}
/**
* Chainable mutator which adds text parameters and MessageParam parameters
*
* @param mixed ...$values Scalar or MessageParam values
* @return MessageValue
*/
public function params( ...$values ) {
foreach ( $values as $value ) {
if ( $value instanceof MessageParam ) {
$this->params[] = $value;
} else {
$this->params[] = new TextParam( ParamType::TEXT, $value );
}
}
return $this;
}
/**
* Chainable mutator which adds text parameters with a common type
*
* @param string $type One of the ParamType constants
* @param mixed ...$values Scalar values
* @return MessageValue
*/
public function textParamsOfType( $type, ...$values ) {
foreach ( $values as $value ) {
$this->params[] = new TextParam( $type, $value );
}
return $this;
}
/**
* Chainable mutator which adds list parameters with a common type
*
* @param string $listType One of the ListType constants
* @param array ...$values Each value should be an array of list items.
* @return MessageValue
*/
public function listParamsOfType( $listType, ...$values ) {
foreach ( $values as $value ) {
$this->params[] = new ListParam( $listType, $value );
}
return $this;
}
/**
* Chainable mutator which adds parameters of type text.
*
* @param string ...$values
* @return MessageValue
*/
public function textParams( ...$values ) {
return $this->textParamsOfType( ParamType::TEXT, ...$values );
}
/**
* Chainable mutator which adds numeric parameters
*
* @param mixed ...$values
* @return MessageValue
*/
public function numParams( ...$values ) {
return $this->textParamsOfType( ParamType::NUM, ...$values );
}
/**
* Chainable mutator which adds parameters which are a duration specified
* in seconds. This is similar to timePeriodParams() except that the result
* will be more verbose.
*
* @param int|float ...$values
* @return MessageValue
*/
public function longDurationParams( ...$values ) {
return $this->textParamsOfType( ParamType::DURATION_LONG, ...$values );
}
/**
* Chainable mutator which adds parameters which are a time period in seconds.
* This is similar to durationParams() except that the result will be more
* compact.
*
* @param int|float ...$values
* @return MessageValue
*/
public function shortDurationParams( ...$values ) {
return $this->textParamsOfType( ParamType::DURATION_SHORT, ...$values );
}
/**
* Chainable mutator which adds parameters which are an expiry timestamp
* as used in the MediaWiki database schema.
*
* @param string ...$values
* @return MessageValue
*/
public function expiryParams( ...$values ) {
return $this->textParamsOfType( ParamType::EXPIRY, ...$values );
}
/**
* Chainable mutator which adds parameters which are a number of bytes.
*
* @param int ...$values
* @return MessageValue
*/
public function sizeParams( ...$values ) {
return $this->textParamsOfType( ParamType::SIZE, ...$values );
}
/**
* Chainable mutator which adds parameters which are a number of bits per
* second.
*
* @param int|float ...$values
* @return MessageValue
*/
public function bitrateParams( ...$values ) {
return $this->textParamsOfType( ParamType::BITRATE, ...$values );
}
/**
* Chainable mutator which adds parameters of type "raw".
*
* @param mixed ...$values
* @return MessageValue
*/
public function rawParams( ...$values ) {
return $this->textParamsOfType( ParamType::RAW, ...$values );
}
/**
* Chainable mutator which adds parameters of type "plaintext".
*/
public function plaintextParams( ...$values ) {
return $this->textParamsOfType( ParamType::PLAINTEXT, ...$values );
}
/**
* Chainable mutator which adds comma lists. Each comma list is an array of
* list elements, and each list element is either a MessageParam or a
* string. String parameters are converted to parameters of type "text".
*
* The list parameters thus created are formatted as a comma-separated list,
* or some local equivalent.
*
* @param (MessageParam|string)[] ...$values
* @return MessageValue
*/
public function commaListParams( ...$values ) {
return $this->listParamsOfType( ListType::COMMA, ...$values );
}
/**
* Chainable mutator which adds semicolon lists. Each semicolon list is an
* array of list elements, and each list element is either a MessageParam
* or a string. String parameters are converted to parameters of type
* "text".
*
* The list parameters thus created are formatted as a semicolon-separated
* list, or some local equivalent.
*
* @param (MessageParam|string)[] ...$values
* @return MessageValue
*/
public function semicolonListParams( ...$values ) {
return $this->listParamsOfType( ListType::SEMICOLON, ...$values );
}
/**
* Chainable mutator which adds pipe lists. Each pipe list is an array of
* list elements, and each list element is either a MessageParam or a
* string. String parameters are converted to parameters of type "text".
*
* The list parameters thus created are formatted as a pipe ("|") -separated
* list, or some local equivalent.
*
* @param (MessageParam|string)[] ...$values
* @return MessageValue
*/
public function pipeListParams( ...$values ) {
return $this->listParamsOfType( ListType::PIPE, ...$values );
}
/**
* Chainable mutator which adds text lists. Each text list is an array of
* list elements, and each list element is either a MessageParam or a
* string. String parameters are converted to parameters of type "text".
*
* The list parameters thus created, when formatted, are joined as in natural
* language. In English, this means a comma-separated list, with the last
* two elements joined with "and".
*
* @param (MessageParam|string)[] ...$values
* @return MessageValue
*/
public function textListParams( ...$values ) {
return $this->listParamsOfType( ListType::AND, ...$values );
}
/**
* Dump the object for testing/debugging
*
* @return string
*/
public function dump() {
$contents = '';
foreach ( $this->params as $param ) {
$contents .= $param->dump();
}
return '<message key="' . htmlspecialchars( $this->key ) . '">' .
$contents . '</message>';
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Wikimedia\Message;
/**
* The constants used to specify parameter types. The values of the constants
* are an unstable implementation detail, and correspond to the names of the
* parameter types in the Message class.
*/
class ParamType {
/** A simple text parameter */
const TEXT = 'text';
/** A number, to be formatted using local digits and separators */
const NUM = 'num';
/** A number of seconds, to be formatted as natural language text. */
const DURATION_LONG = 'duration';
/** A number of seconds, to be formatted in an abbreviated way. */
const DURATION_SHORT = 'timeperiod';
/**
* An expiry time for a block. The input is either a timestamp in one
* of the formats accepted by the Wikimedia\Timestamp library, or
* "infinity" for an infinite block.
*/
const EXPIRY = 'expiry';
/** A number of bytes. */
const SIZE = 'size';
/** A number of bits per second. */
const BITRATE = 'bitrate';
/** The list type (ListParam) */
const LIST = 'list';
/**
* A text parameter which is substituted after preprocessing, and so is
* not available to the preprocessor and cannot be modified by it.
*/
const RAW = 'raw';
/** Reserved for future use. */
const PLAINTEXT = 'plaintext';
}

View file

@ -0,0 +1,37 @@
<?php
namespace Wikimedia\Message;
class TextParam extends MessageParam {
/**
* Construct a text parameter
*
* @param string $type May be one of:
* - ParamType::TEXT: A simple text parameter
* - ParamType::NUM: A number, to be formatted using local digits and
* separators
* - ParamType::DURATION_LONG: A number of seconds, to be formatted as natural
* language text.
* - ParamType::DURATION_SHORT: A number of seconds, to be formatted in an
* abbreviated way.
* - ParamType::EXPIRY: An expiry time for a block. The input is either
* a timestamp in one of the formats accepted by the Wikimedia\Timestamp
* library, or "infinity" for an infinite block.
* - ParamType::SIZE: A number of bytes.
* - ParamType::BITRATE: A number of bits per second.
* - ParamType::RAW: A text parameter which is substituted after
* preprocessing, and so is not available to the preprocessor and cannot
* be modified by it.
* - ParamType::PLAINTEXT: Reserved for future use.
*
* @param string|int|float $value
*/
public function __construct( $type, $value ) {
$this->type = $type;
$this->value = $value;
}
public function dump() {
return "<{$this->type}>" . htmlspecialchars( $this->value ) . "</{$this->type}>";
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace MediaWiki\Tests\Message;
use MediaWiki\Message\TextFormatter;
use MediaWikiTestCase;
use Message;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Wikimedia\Message\TextParam;
/**
* @covers \MediaWiki\Message\TextFormatter
* @covers \Wikimedia\Message\MessageValue
* @covers \Wikimedia\Message\ListParam
* @covers \Wikimedia\Message\TextParam
* @covers \Wikimedia\Message\MessageParam
*/
class TextFormatterTest extends MediaWikiTestCase {
private function createTextFormatter( $langCode ) {
return new class( $langCode ) extends TextFormatter {
public function __construct( $langCode ) {
parent::__construct( $langCode );
}
protected function createMessage( $key ) {
return new FakeMessage( $key );
}
};
}
public function testGetLangCode() {
$formatter = $this->createTextFormatter( 'fr' );
$this->assertSame( 'fr', $formatter->getLangCode() );
}
public function testFormatBitrate() {
$formatter = $this->createTextFormatter( 'en' );
$mv = ( new MessageValue( 'test' ) )->bitrateParams( 100, 200 );
$result = $formatter->format( $mv );
$this->assertSame( 'test 100 bps 200 bps', $result );
}
public function testFormatList() {
$formatter = $this->createTextFormatter( 'en' );
$mv = ( new MessageValue( 'test' ) )->commaListParams( [
'a',
new TextParam( ParamType::BITRATE, 100 ),
] );
$result = $formatter->format( $mv );
$this->assertSame( 'test a, 100 bps $2', $result );
}
}
class FakeMessage extends Message {
public function fetchMessage() {
return "{$this->getKey()} $1 $2";
}
}

View file

@ -0,0 +1,219 @@
<?php
namespace Wikimedia\Tests\Message;
use Wikimedia\Message\ListType;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Wikimedia\Message\TextParam;
use MediaWikiTestCase;
/**
* @covers \Wikimedia\Message\MessageValue
* @covers \Wikimedia\Message\ListParam
* @covers \Wikimedia\Message\TextParam
* @covers \Wikimedia\Message\MessageParam
*/
class MessageValueTest extends MediaWikiTestCase {
public static function provideConstruct() {
return [
[
[],
'<message key="key"></message>',
],
[
[ 'a' ],
'<message key="key"><text>a</text></message>'
],
[
[ new TextParam( ParamType::BITRATE, 100 ) ],
'<message key="key"><bitrate>100</bitrate></message>'
],
];
}
/** @dataProvider provideConstruct */
public function testConstruct( $input, $expected ) {
$mv = new MessageValue( 'key', $input );
$this->assertSame( $expected, $mv->dump() );
}
public function testGetKey() {
$mv = new MessageValue( 'key' );
$this->assertSame( 'key', $mv->getKey() );
}
public function testParams() {
$mv = new MessageValue( 'key' );
$mv->params( 1, 'x' );
$mv2 = $mv->params( new TextParam( ParamType::BITRATE, 100 ) );
$this->assertSame(
'<message key="key"><text>1</text><text>x</text><bitrate>100</bitrate></message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testTextParamsOfType() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->textParamsOfType( ParamType::BITRATE, 1, 2 );
$this->assertSame( '<message key="key">' .
'<bitrate>1</bitrate><bitrate>2</bitrate>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testListParamsOfType() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->listParamsOfType( ListType::COMMA, [ 'a' ], [ 'b', 'c' ] );
$this->assertSame( '<message key="key">' .
'<list listType="comma"><text>a</text></list>' .
'<list listType="comma"><text>b</text><text>c</text></list>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testTextParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->textParams( 'a', 'b' );
$this->assertSame( '<message key="key">' .
'<text>a</text>' .
'<text>b</text>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testNumParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->numParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<num>1</num>' .
'<num>2</num>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testLongDurationParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->longDurationParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<duration>1</duration>' .
'<duration>2</duration>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testShortDurationParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->shortDurationParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<timeperiod>1</timeperiod>' .
'<timeperiod>2</timeperiod>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testExpiryParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->expiryParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<expiry>1</expiry>' .
'<expiry>2</expiry>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testSizeParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->sizeParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<size>1</size>' .
'<size>2</size>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testBitrateParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->bitrateParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<bitrate>1</bitrate>' .
'<bitrate>2</bitrate>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testRawParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->rawParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<raw>1</raw>' .
'<raw>2</raw>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testPlaintextParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->plaintextParams( 1, 2 );
$this->assertSame( '<message key="key">' .
'<plaintext>1</plaintext>' .
'<plaintext>2</plaintext>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testCommaListParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->commaListParams( [ 'a', 'b' ] );
$this->assertSame( '<message key="key">' .
'<list listType="comma">' .
'<text>a</text><text>b</text>' .
'</list></message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function tesSemicolonListParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->semicolonListParams( [ 'a', 'b' ] );
$this->assertSame( '<message key="key">' .
'<list listType="semicolon">' .
'<text>a</text><text>b</text>' .
'</list></message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testPipeListParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->pipeListParams( [ 'a', 'b' ] );
$this->assertSame( '<message key="key">' .
'<list listType="pipe">' .
'<text>a</text><text>b</text>' .
'</list></message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testTextListParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->textListParams( [ 'a', 'b' ] );
$this->assertSame( '<message key="key">' .
'<list listType="text">' .
'<text>a</text><text>b</text>' .
'</list></message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
}