'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/',
'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/',
];
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;
return $this->getService( 'MessageCache' );
}
+ /**
+ * @since 1.34
+ * @return IMessageFormatterFactory
+ */
+ public function getMessageFormatterFactory() {
+ return $this->getService( 'MessageFormatterFactory' );
+ }
+
/**
* @since 1.28
* @return MimeAnalyzer
--- /dev/null
+<?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];
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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 );
+ }
+}
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;
);
},
+ 'MessageFormatterFactory' =>
+ function ( MediaWikiServices $services ) : IMessageFormatterFactory {
+ return new MessageFormatterFactory();
+ },
+
'MimeAnalyzer' => function ( MediaWikiServices $services ) : MimeAnalyzer {
$logger = LoggerFactory::getInstance( 'Mime' );
$mainConfig = $services->getMainConfig();
--- /dev/null
+<?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;
+}
--- /dev/null
+<?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 );
+}
--- /dev/null
+<?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}>";
+ }
+}
--- /dev/null
+<?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';
+}
--- /dev/null
+<?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();
+}
--- /dev/null
+<?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>';
+ }
+}
--- /dev/null
+<?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';
+}
--- /dev/null
+<?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}>";
+ }
+}
--- /dev/null
+<?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";
+ }
+}
--- /dev/null
+<?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 );
+ }
+}