Merge branch '4.3' into 4.4

* 4.3:
  [Routing] Add a param annotation for $annot.
  [DI] fix docblock
  [Console] fix docblock
  Add missing translations for Armenian locale
  [Process] Added missing return type.
  [Process] Doc block backport.
  Added doc block for Registry::supports().
  [Cache] Fix predis test
  Don't duplicate addresses in Sendgrid Transport
  Remove unnecessary statement
  Fix some docblocks.
  [Messenger] make delay exchange and queues durable like the normal ones by default
  Cancel delayed message if handler fails
  Added tests for #32370
This commit is contained in:
Nicolas Grekas 2019-08-19 13:17:23 +02:00
commit 3cd20c993d
16 changed files with 250 additions and 30 deletions

View File

@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface; use Symfony\Component\Console\Style\StyleInterface;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
@ -26,6 +27,9 @@ use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
*/ */
abstract class AbstractConfigCommand extends ContainerDebugCommand abstract class AbstractConfigCommand extends ContainerDebugCommand
{ {
/**
* @param OutputInterface|StyleInterface $output
*/
protected function listBundles($output) protected function listBundles($output)
{ {
$title = 'Available registered bundles with their extension alias if available'; $title = 'Available registered bundles with their extension alias if available';

View File

@ -111,8 +111,8 @@ class CookieJar
/** /**
* Updates the cookie jar from a response Set-Cookie headers. * Updates the cookie jar from a response Set-Cookie headers.
* *
* @param array $setCookies Set-Cookie headers from an HTTP response * @param string[] $setCookies Set-Cookie headers from an HTTP response
* @param string $uri The base URL * @param string $uri The base URL
*/ */
public function updateFromSetCookie(array $setCookies, $uri = null) public function updateFromSetCookie(array $setCookies, $uri = null)
{ {

View File

@ -34,7 +34,7 @@ class PredisAdapterTest extends AbstractRedisAdapterTest
$params = [ $params = [
'scheme' => 'tcp', 'scheme' => 'tcp',
'host' => 'localhost', 'host' => $redisHost,
'port' => 6379, 'port' => 6379,
'persistent' => 0, 'persistent' => 0,
'timeout' => 3, 'timeout' => 3,

View File

@ -110,7 +110,7 @@ trait TesterTrait
* @param array $inputs An array of strings representing each input * @param array $inputs An array of strings representing each input
* passed to the command input stream * passed to the command input stream
* *
* @return self * @return $this
*/ */
public function setInputs(array $inputs) public function setInputs(array $inputs)
{ {

View File

@ -89,7 +89,7 @@ class ChildDefinition extends Definition
* @param int|string $index * @param int|string $index
* @param mixed $value * @param mixed $value
* *
* @return self the current instance * @return $this
* *
* @throws InvalidArgumentException when $index isn't an integer * @throws InvalidArgumentException when $index isn't an integer
*/ */

View File

@ -1208,7 +1208,6 @@ EOF;
if (!$id->isDeprecated()) { if (!$id->isDeprecated()) {
continue; continue;
} }
$id = (string) $id;
$code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n"; $code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n";
} }

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mailer\Bridge\Sendgrid\Tests\Http\Api;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Bridge\Sendgrid\Http\Api\SendgridTransport;
use Symfony\Component\Mime\Email;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class SendgridTransportTest extends TestCase
{
public function testSend()
{
$email = new Email();
$email->from('foo@example.com')
->to('bar@example.com')
->bcc('baz@example.com');
$response = $this->createMock(ResponseInterface::class);
$response
->expects($this->once())
->method('getStatusCode')
->willReturn(202);
$httpClient = $this->createMock(HttpClientInterface::class);
$httpClient
->expects($this->once())
->method('request')
->with('POST', 'https://api.sendgrid.com/v3/mail/send', [
'json' => [
'personalizations' => [
[
'to' => [['email' => 'bar@example.com']],
'subject' => null,
'bcc' => [['email' => 'baz@example.com']],
],
],
'from' => ['email' => 'foo@example.com'],
'content' => [],
],
'auth_bearer' => 'foo',
])
->willReturn($response);
$mailer = new SendgridTransport('foo', $httpClient);
$mailer->send($email);
}
}

View File

@ -73,7 +73,7 @@ class SendgridApiTransport extends AbstractApiTransport
} }
$personalization = [ $personalization = [
'to' => array_map($addressStringifier, $this->getRecipients($email, $envelope)), 'to' => array_map($addressStringifier, $email->getTo()),
'subject' => $email->getSubject(), 'subject' => $email->getSubject(),
]; ];
if ($emails = array_map($addressStringifier, $email->getCc())) { if ($emails = array_map($addressStringifier, $email->getCc())) {

View File

@ -81,12 +81,16 @@ class DispatchAfterCurrentBusMiddleware implements MiddlewareInterface
// "Root dispatch" call is finished, dispatch stored messages. // "Root dispatch" call is finished, dispatch stored messages.
$exceptions = []; $exceptions = [];
while (null !== $queueItem = array_shift($this->queue)) { while (null !== $queueItem = array_shift($this->queue)) {
// Save how many messages are left in queue before handling the message
$queueLengthBefore = \count($this->queue);
try { try {
// Execute the stored messages // Execute the stored messages
$queueItem->getStack()->next()->handle($queueItem->getEnvelope(), $queueItem->getStack()); $queueItem->getStack()->next()->handle($queueItem->getEnvelope(), $queueItem->getStack());
} catch (\Exception $exception) { } catch (\Exception $exception) {
// Gather all exceptions // Gather all exceptions
$exceptions[] = $exception; $exceptions[] = $exception;
// Restore queue to previous state
$this->queue = \array_slice($this->queue, 0, $queueLengthBefore);
} }
} }

View File

@ -99,6 +99,86 @@ class DispatchAfterCurrentBusMiddlewareTest extends TestCase
$messageBus->dispatch($message); $messageBus->dispatch($message);
} }
public function testLongChainWithExceptions()
{
$command = new DummyMessage('Level 0');
$eventL1a = new DummyEvent('Event level 1A');
$eventL1b = new DummyEvent('Event level 1B'); // will dispatch 2 more events
$eventL1c = new DummyEvent('Event level 1C');
$eventL2a = new DummyEvent('Event level 2A'); // Will dispatch 1 event and throw exception
$eventL2b = new DummyEvent('Event level 2B'); // Will dispatch 1 event
$eventL3a = new DummyEvent('Event level 3A'); // This should never get handled.
$eventL3b = new DummyEvent('Event level 3B');
$middleware = new DispatchAfterCurrentBusMiddleware();
$handlingMiddleware = $this->createMock(MiddlewareInterface::class);
$eventBus = new MessageBus([
$middleware,
$handlingMiddleware,
]);
// The command bus will dispatch 3 events.
$commandBus = new MessageBus([
$middleware,
new DispatchingMiddleware($eventBus, [
new Envelope($eventL1a, [new DispatchAfterCurrentBusStamp()]),
new Envelope($eventL1b, [new DispatchAfterCurrentBusStamp()]),
new Envelope($eventL1c, [new DispatchAfterCurrentBusStamp()]),
]),
$handlingMiddleware,
]);
// Expect main dispatched message to be handled first:
$this->expectHandledMessage($handlingMiddleware, 0, $command);
$this->expectHandledMessage($handlingMiddleware, 1, $eventL1a);
// Handling $eventL1b will dispatch 2 more events
$handlingMiddleware->expects($this->at(2))->method('handle')->with($this->callback(function (Envelope $envelope) use ($eventL1b) {
return $envelope->getMessage() === $eventL1b;
}))->willReturnCallback(function ($envelope, StackInterface $stack) use ($eventBus, $eventL2a, $eventL2b) {
$envelope1 = new Envelope($eventL2a, [new DispatchAfterCurrentBusStamp()]);
$eventBus->dispatch($envelope1);
$eventBus->dispatch(new Envelope($eventL2b, [new DispatchAfterCurrentBusStamp()]));
return $stack->next()->handle($envelope, $stack);
});
$this->expectHandledMessage($handlingMiddleware, 3, $eventL1c);
// Handle $eventL2a will dispatch event and throw exception
$handlingMiddleware->expects($this->at(4))->method('handle')->with($this->callback(function (Envelope $envelope) use ($eventL2a) {
return $envelope->getMessage() === $eventL2a;
}))->willReturnCallback(function ($envelope, StackInterface $stack) use ($eventBus, $eventL3a) {
$eventBus->dispatch(new Envelope($eventL3a, [new DispatchAfterCurrentBusStamp()]));
throw new \RuntimeException('Some exception while handling Event level 2a');
});
// Make sure $eventL2b is handled, since it was dispatched from $eventL1b
$handlingMiddleware->expects($this->at(5))->method('handle')->with($this->callback(function (Envelope $envelope) use ($eventL2b) {
return $envelope->getMessage() === $eventL2b;
}))->willReturnCallback(function ($envelope, StackInterface $stack) use ($eventBus, $eventL3b) {
$eventBus->dispatch(new Envelope($eventL3b, [new DispatchAfterCurrentBusStamp()]));
return $stack->next()->handle($envelope, $stack);
});
// We dont handle exception L3a since L2a threw an exception.
$this->expectHandledMessage($handlingMiddleware, 6, $eventL3b);
// Note: $eventL3a should not be handled.
$this->expectException(DelayedMessageHandlingException::class);
$this->expectExceptionMessage('RuntimeException: Some exception while handling Event level 2a');
$commandBus->dispatch($command);
}
public function testHandleDelayedEventFromQueue() public function testHandleDelayedEventFromQueue()
{ {
$message = new DummyMessage('Hello'); $message = new DummyMessage('Hello');

View File

@ -364,7 +364,7 @@ class ConnectionTest extends TestCase
$amqpQueue->expects($this->once())->method('setName')->with(self::DEFAULT_EXCHANGE_NAME); $amqpQueue->expects($this->once())->method('setName')->with(self::DEFAULT_EXCHANGE_NAME);
$amqpQueue->expects($this->once())->method('declareQueue'); $amqpQueue->expects($this->once())->method('declareQueue');
$delayExchange->expects($this->once())->method('setName')->with('delay'); $delayExchange->expects($this->once())->method('setName')->with('delays');
$delayExchange->expects($this->once())->method('declareExchange'); $delayExchange->expects($this->once())->method('declareExchange');
$delayExchange->expects($this->once())->method('publish'); $delayExchange->expects($this->once())->method('publish');
@ -398,7 +398,7 @@ class ConnectionTest extends TestCase
]); ]);
$delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('declareQueue');
$delayQueue->expects($this->once())->method('bind')->with('delay', 'delay_messages__5000'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages__5000');
$delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__5000', AMQP_NOPARAM, ['headers' => ['x-some-headers' => 'foo']]); $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__5000', AMQP_NOPARAM, ['headers' => ['x-some-headers' => 'foo']]);
@ -440,7 +440,7 @@ class ConnectionTest extends TestCase
]); ]);
$delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('declareQueue');
$delayQueue->expects($this->once())->method('bind')->with('delay', 'delay_messages__120000'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages__120000');
$delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__120000', AMQP_NOPARAM, ['headers' => []]); $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages__120000', AMQP_NOPARAM, ['headers' => []]);
$connection->publish('{}', [], 120000); $connection->publish('{}', [], 120000);
@ -544,7 +544,7 @@ class ConnectionTest extends TestCase
]); ]);
$delayQueue->expects($this->once())->method('declareQueue'); $delayQueue->expects($this->once())->method('declareQueue');
$delayQueue->expects($this->once())->method('bind')->with('delay', 'delay_messages_routing_key_120000'); $delayQueue->expects($this->once())->method('bind')->with('delays', 'delay_messages_routing_key_120000');
$delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages_routing_key_120000', AMQP_NOPARAM, ['headers' => []]); $delayExchange->expects($this->once())->method('publish')->with('{}', 'delay_messages_routing_key_120000', AMQP_NOPARAM, ['headers' => []]);
$connection->publish('{}', [], 120000, new AmqpStamp('routing_key')); $connection->publish('{}', [], 120000, new AmqpStamp('routing_key'));

View File

@ -60,7 +60,7 @@ class Connection
{ {
$this->connectionOptions = array_replace_recursive([ $this->connectionOptions = array_replace_recursive([
'delay' => [ 'delay' => [
'exchange_name' => 'delay', 'exchange_name' => 'delays',
'queue_name_pattern' => 'delay_%exchange_name%_%routing_key%_%delay%', 'queue_name_pattern' => 'delay_%exchange_name%_%routing_key%_%delay%',
], ],
], $connectionOptions); ], $connectionOptions);
@ -92,7 +92,7 @@ class Connection
* * arguments: Extra arguments * * arguments: Extra arguments
* * delay: * * delay:
* * queue_name_pattern: Pattern to use to create the queues (Default: "delay_%exchange_name%_%routing_key%_%delay%") * * queue_name_pattern: Pattern to use to create the queues (Default: "delay_%exchange_name%_%routing_key%_%delay%")
* * exchange_name: Name of the exchange to be used for the delayed/retried messages (Default: "delay") * * exchange_name: Name of the exchange to be used for the delayed/retried messages (Default: "delays")
* * auto_setup: Enable or not the auto-setup of queues and exchanges (Default: true) * * auto_setup: Enable or not the auto-setup of queues and exchanges (Default: true)
* * prefetch_count: set channel prefetch count * * prefetch_count: set channel prefetch count
*/ */
@ -251,6 +251,11 @@ class Connection
$this->amqpDelayExchange = $this->amqpFactory->createExchange($this->channel()); $this->amqpDelayExchange = $this->amqpFactory->createExchange($this->channel());
$this->amqpDelayExchange->setName($this->connectionOptions['delay']['exchange_name']); $this->amqpDelayExchange->setName($this->connectionOptions['delay']['exchange_name']);
$this->amqpDelayExchange->setType(AMQP_EX_TYPE_DIRECT); $this->amqpDelayExchange->setType(AMQP_EX_TYPE_DIRECT);
if ('delays' === $this->connectionOptions['delay']['exchange_name']) {
// only add the new flag when the name was not provided explicitly so we're using the new default name to prevent a redeclaration error
// the condition will be removed in 4.4
$this->amqpDelayExchange->setFlags(AMQP_DURABLE);
}
} }
return $this->amqpDelayExchange; return $this->amqpDelayExchange;
@ -273,16 +278,24 @@ class Connection
[$delay, $this->exchangeOptions['name'], $routingKey ?? ''], [$delay, $this->exchangeOptions['name'], $routingKey ?? ''],
$this->connectionOptions['delay']['queue_name_pattern'] $this->connectionOptions['delay']['queue_name_pattern']
)); ));
if ('delay_%exchange_name%_%routing_key%_%delay%' === $this->connectionOptions['delay']['queue_name_pattern']) {
// the condition will be removed in 4.4
$queue->setFlags(AMQP_DURABLE);
$extraArguments = [
// delete the delay queue 10 seconds after the message expires
// publishing another message redeclares the queue which renews the lease
'x-expires' => $delay + 10000,
];
} else {
$extraArguments = [];
}
$queue->setArguments([ $queue->setArguments([
'x-message-ttl' => $delay, 'x-message-ttl' => $delay,
// delete the delay queue 10 seconds after the message expires
// publishing another message redeclares the queue which renews the lease
'x-expires' => $delay + 10000,
'x-dead-letter-exchange' => $this->exchangeOptions['name'], 'x-dead-letter-exchange' => $this->exchangeOptions['name'],
// after being released from to DLX, make sure the original routing key will be used // after being released from to DLX, make sure the original routing key will be used
// we must use an empty string instead of null for the argument to be picked up // we must use an empty string instead of null for the argument to be picked up
'x-dead-letter-routing-key' => $routingKey ?? '', 'x-dead-letter-routing-key' => $routingKey ?? '',
]); ] + $extraArguments);
return $queue; return $queue;
} }

View File

@ -186,6 +186,8 @@ class Process implements \IteratorAggregate
* @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input * @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input
* @param int|float|null $timeout The timeout in seconds or null to disable * @param int|float|null $timeout The timeout in seconds or null to disable
* *
* @return static
*
* @throws RuntimeException When proc_open is not installed * @throws RuntimeException When proc_open is not installed
*/ */
public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60) public static function fromShellCommandline(string $command, string $cwd = null, array $env = null, $input = null, ?float $timeout = 60)
@ -240,7 +242,7 @@ class Process implements \IteratorAggregate
* This is identical to run() except that an exception is thrown if the process * This is identical to run() except that an exception is thrown if the process
* exits with a non-zero exit code. * exits with a non-zero exit code.
* *
* @return self * @return $this
* *
* @throws ProcessFailedException if the process didn't terminate successfully * @throws ProcessFailedException if the process didn't terminate successfully
* *
@ -967,7 +969,7 @@ class Process implements \IteratorAggregate
* *
* @param string|array $commandline The command to execute * @param string|array $commandline The command to execute
* *
* @return self The current Process instance * @return $this
* *
* @deprecated since Symfony 4.2. * @deprecated since Symfony 4.2.
*/ */
@ -1001,13 +1003,13 @@ class Process implements \IteratorAggregate
} }
/** /**
* Sets the process timeout (max. runtime). * Sets the process timeout (max. runtime) in seconds.
* *
* To disable the timeout, set this value to null. * To disable the timeout, set this value to null.
* *
* @param int|float|null $timeout The timeout in seconds * @param int|float|null $timeout The timeout in seconds
* *
* @return self The current Process instance * @return $this
* *
* @throws InvalidArgumentException if the timeout is negative * @throws InvalidArgumentException if the timeout is negative
*/ */
@ -1025,7 +1027,7 @@ class Process implements \IteratorAggregate
* *
* @param int|float|null $timeout The timeout in seconds * @param int|float|null $timeout The timeout in seconds
* *
* @return self The current Process instance * @return $this
* *
* @throws LogicException if the output is disabled * @throws LogicException if the output is disabled
* @throws InvalidArgumentException if the timeout is negative * @throws InvalidArgumentException if the timeout is negative
@ -1046,7 +1048,7 @@ class Process implements \IteratorAggregate
* *
* @param bool $tty True to enabled and false to disable * @param bool $tty True to enabled and false to disable
* *
* @return self The current Process instance * @return $this
* *
* @throws RuntimeException In case the TTY mode is not supported * @throws RuntimeException In case the TTY mode is not supported
*/ */
@ -1080,7 +1082,7 @@ class Process implements \IteratorAggregate
* *
* @param bool $bool * @param bool $bool
* *
* @return self * @return $this
*/ */
public function setPty($bool) public function setPty($bool)
{ {
@ -1120,7 +1122,7 @@ class Process implements \IteratorAggregate
* *
* @param string $cwd The new working directory * @param string $cwd The new working directory
* *
* @return self The current Process instance * @return $this
*/ */
public function setWorkingDirectory($cwd) public function setWorkingDirectory($cwd)
{ {
@ -1152,7 +1154,7 @@ class Process implements \IteratorAggregate
* *
* @param array $env The new environment variables * @param array $env The new environment variables
* *
* @return self The current Process instance * @return $this
*/ */
public function setEnv(array $env) public function setEnv(array $env)
{ {
@ -1183,7 +1185,7 @@ class Process implements \IteratorAggregate
* *
* @param string|int|float|bool|resource|\Traversable|null $input The content * @param string|int|float|bool|resource|\Traversable|null $input The content
* *
* @return self The current Process instance * @return $this
* *
* @throws LogicException In case the process is running * @throws LogicException In case the process is running
*/ */
@ -1203,7 +1205,7 @@ class Process implements \IteratorAggregate
* *
* @param bool $inheritEnv * @param bool $inheritEnv
* *
* @return self The current Process instance * @return $this
* *
* @deprecated since Symfony 4.4, env variables are always inherited * @deprecated since Symfony 4.4, env variables are always inherited
*/ */

View File

@ -15,6 +15,7 @@ use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
@ -129,6 +130,10 @@ abstract class AnnotationClassLoader implements LoaderInterface
return $collection; return $collection;
} }
/**
* @param RouteAnnotation $annot or an object that exposes a similar interface
* @param array $globals
*/
protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method) protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method)
{ {
$name = $annot->getName(); $name = $annot->getName();

View File

@ -43,8 +43,8 @@ class LogoutUrlGenerator
* *
* @param string $key The firewall key * @param string $key The firewall key
* @param string $logoutPath The path that starts the logout process * @param string $logoutPath The path that starts the logout process
* @param string $csrfTokenId The ID of the CSRF token * @param string|null $csrfTokenId The ID of the CSRF token
* @param string $csrfParameter The CSRF token parameter name * @param string|null $csrfParameter The CSRF token parameter name
* @param string|null $context The listener context * @param string|null $context The listener context
*/ */
public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null, string $context = null) public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null, string $context = null)

View File

@ -314,6 +314,58 @@
<source>This is not a valid Business Identifier Code (BIC).</source> <source>This is not a valid Business Identifier Code (BIC).</source>
<target>Սա վավեր Business Identifier Code (BIC) չէ։</target> <target>Սա վավեր Business Identifier Code (BIC) չէ։</target>
</trans-unit> </trans-unit>
<trans-unit id="82">
<source>Error</source>
<target>Սխալ</target>
</trans-unit>
<trans-unit id="83">
<source>This is not a valid UUID.</source>
<target>Սա վավեր UUID չէ:</target>
</trans-unit>
<trans-unit id="84">
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Այս արժեքը պետք է լինի բազմակի {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Բիզնեսի նույնականացման կոդը (BIC) կապված չէ IBAN- ի հետ {{ iban }}.</target>
</trans-unit>
<trans-unit id="86">
<source>This value should be valid JSON.</source>
<target>Այս արժեքը պետք է լինի վավեր JSON:</target>
</trans-unit>
<trans-unit id="87">
<source>This collection should contain only unique elements.</source>
<target>Այս հավաքածուն պետք է պարունակի միայն եզակի տարրեր:</target>
</trans-unit>
<trans-unit id="88">
<source>This value should be positive.</source>
<target>Այս արժեքը պետք է լինի դրական:</target>
</trans-unit>
<trans-unit id="89">
<source>This value should be either positive or zero.</source>
<target>Այս արժեքը պետք է լինի դրական կամ զրոյական:</target>
</trans-unit>
<trans-unit id="90">
<source>This value should be negative.</source>
<target>Այս արժեքը պետք է լինի բացասական:</target>
</trans-unit>
<trans-unit id="91">
<source>This value should be either negative or zero.</source>
<target>Այս արժեքը պետք է լինի բացասական կամ զրոյական:</target>
</trans-unit>
<trans-unit id="92">
<source>This value is not a valid timezone.</source>
<target>Այս արժեքը վավեր ժամանակի գոտի չէ:</target>
</trans-unit>
<trans-unit id="93">
<source>This password has been leaked in a data breach, it must not be used. Please use another password.</source>
<target>Այս գաղտնաբառն արտահոսվել է տվյալների խախտման մեջ, այն չպետք է օգտագործվի: Խնդրում ենք օգտագործել մեկ այլ գաղտնաբառ:</target>
</trans-unit>
<trans-unit id="94">
<source>This value should be between {{ min }} and {{ max }}.</source>
<target>Այս արժեքը պետք է լինի միջև {{ min }} և {{ max }}.</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>