Merge branch '3.4' into 4.2

* 3.4:
  Removed non-existing parameters for LogoutUrlGenerator calls
  [HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy
  [Validator] Add the missing translations for the Latvian ("lv") locale
  Fixed the DebugClassLoader compatibility with eval()'d code on Darwin
  [Validator] Update Serbian translation file
This commit is contained in:
Fabien Potencier 2019-03-03 19:11:24 +01:00
commit c52dcc40a8
8 changed files with 519 additions and 84 deletions

View File

@ -11,7 +11,6 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\Templating\Helper\Helper;
@ -38,7 +37,7 @@ class LogoutUrlHelper extends Helper
*/
public function getLogoutPath($key)
{
return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH);
return $this->generator->getLogoutPath($key);
}
/**
@ -50,7 +49,7 @@ class LogoutUrlHelper extends Helper
*/
public function getLogoutUrl($key)
{
return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL);
return $this->generator->getLogoutUrl($key);
}
/**

View File

@ -427,6 +427,11 @@ class DebugClassLoader
$dirFiles = self::$darwinCache[$kDir][1];
if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) {
// Get the file name from "file_name.php(123) : eval()'d code"
$file = substr($file, 0, strrpos($file, '(', -17));
}
if (isset($dirFiles[$file])) {
return $real .= $dirFiles[$file];
}

View File

@ -311,6 +311,11 @@ class DebugClassLoaderTest extends TestCase
$this->assertSame([], $deprecations);
}
public function testEvaluatedCode()
{
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true));
}
}
class ClassLoader

View File

@ -0,0 +1,11 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
eval('
namespace Symfony\Component\Debug\Tests\Fixtures;
class DefinitionInEvaluatedCode
{
}
');

View File

@ -5,10 +5,6 @@
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This code is partially based on the Rack-Cache library by Ryan Tomayko,
* which is released under the MIT license.
* (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801)
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@ -28,30 +24,69 @@ use Symfony\Component\HttpFoundation\Response;
*/
class ResponseCacheStrategy implements ResponseCacheStrategyInterface
{
private $cacheable = true;
/**
* Cache-Control headers that are sent to the final response if they appear in ANY of the responses.
*/
private static $overrideDirectives = ['private', 'no-cache', 'no-store', 'no-transform', 'must-revalidate', 'proxy-revalidate'];
/**
* Cache-Control headers that are sent to the final response if they appear in ALL of the responses.
*/
private static $inheritDirectives = ['public', 'immutable'];
private $embeddedResponses = 0;
private $ttls = [];
private $maxAges = [];
private $isNotCacheableResponseEmbedded = false;
private $age = 0;
private $flagDirectives = [
'no-cache' => null,
'no-store' => null,
'no-transform' => null,
'must-revalidate' => null,
'proxy-revalidate' => null,
'public' => null,
'private' => null,
'immutable' => null,
];
private $ageDirectives = [
'max-age' => null,
's-maxage' => null,
'expires' => null,
];
/**
* {@inheritdoc}
*/
public function add(Response $response)
{
if (!$response->isFresh() || !$response->isCacheable()) {
$this->cacheable = false;
} else {
$maxAge = $response->getMaxAge();
$this->ttls[] = $response->getTtl();
$this->maxAges[] = $maxAge;
++$this->embeddedResponses;
if (null === $maxAge) {
$this->isNotCacheableResponseEmbedded = true;
foreach (self::$overrideDirectives as $directive) {
if ($response->headers->hasCacheControlDirective($directive)) {
$this->flagDirectives[$directive] = true;
}
}
++$this->embeddedResponses;
foreach (self::$inheritDirectives as $directive) {
if (false !== $this->flagDirectives[$directive]) {
$this->flagDirectives[$directive] = $response->headers->hasCacheControlDirective($directive);
}
}
$age = $response->getAge();
$this->age = max($this->age, $age);
if ($this->willMakeFinalResponseUncacheable($response)) {
$this->isNotCacheableResponseEmbedded = true;
return;
}
$this->storeRelativeAgeDirective('max-age', $response->headers->getCacheControlDirective('max-age'), $age);
$this->storeRelativeAgeDirective('s-maxage', $response->headers->getCacheControlDirective('s-maxage') ?: $response->headers->getCacheControlDirective('max-age'), $age);
$expires = $response->getExpires();
$expires = null !== $expires ? $expires->format('U') - $response->getDate()->format('U') : null;
$this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0);
}
/**
@ -64,33 +99,124 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
return;
}
// Remove validation related headers in order to avoid browsers using
// their own cache, because some of the response content comes from
// at least one embedded response (which likely has a different caching strategy).
if ($response->isValidateable()) {
$response->setEtag(null);
$response->setLastModified(null);
}
// Remove validation related headers of the master response,
// because some of the response content comes from at least
// one embedded response (which likely has a different caching strategy).
$response->setEtag(null);
$response->setLastModified(null);
if (!$response->isFresh() || !$response->isCacheable()) {
$this->cacheable = false;
}
$this->add($response);
if (!$this->cacheable) {
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
$response->headers->set('Age', $this->age);
if ($this->isNotCacheableResponseEmbedded) {
$response->setExpires($response->getDate());
if ($this->flagDirectives['no-store']) {
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
} else {
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
}
return;
}
$this->ttls[] = $response->getTtl();
$this->maxAges[] = $response->getMaxAge();
$flags = array_filter($this->flagDirectives);
if ($this->isNotCacheableResponseEmbedded) {
$response->headers->removeCacheControlDirective('s-maxage');
} elseif (null !== $maxAge = min($this->maxAges)) {
$response->setSharedMaxAge($maxAge);
$response->headers->set('Age', $maxAge - min($this->ttls));
if (isset($flags['must-revalidate'])) {
$flags['no-cache'] = true;
}
$response->headers->set('Cache-Control', implode(', ', array_keys($flags)));
$maxAge = null;
$sMaxage = null;
if (\is_numeric($this->ageDirectives['max-age'])) {
$maxAge = $this->ageDirectives['max-age'] + $this->age;
$response->headers->addCacheControlDirective('max-age', $maxAge);
}
if (\is_numeric($this->ageDirectives['s-maxage'])) {
$sMaxage = $this->ageDirectives['s-maxage'] + $this->age;
if ($maxAge !== $sMaxage) {
$response->headers->addCacheControlDirective('s-maxage', $sMaxage);
}
}
if (\is_numeric($this->ageDirectives['expires'])) {
$date = clone $response->getDate();
$date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
$response->setExpires($date);
}
}
/**
* RFC2616, Section 13.4.
*
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
*
* @return bool
*/
private function willMakeFinalResponseUncacheable(Response $response)
{
// RFC2616: A response received with a status code of 200, 203, 300, 301 or 410
// MAY be stored by a cache […] unless a cache-control directive prohibits caching.
if ($response->headers->hasCacheControlDirective('no-cache')
|| $response->headers->getCacheControlDirective('no-store')
) {
return true;
}
// Last-Modified and Etag headers cannot be merged, they render the response uncacheable
// by default (except if the response also has max-age etc.).
if (\in_array($response->getStatusCode(), [200, 203, 300, 301, 410])
&& null === $response->getLastModified()
&& null === $response->getEtag()
) {
return false;
}
// RFC2616: A response received with any other status code (e.g. status codes 302 and 307)
// MUST NOT be returned in a reply to a subsequent request unless there are
// cache-control directives or another header(s) that explicitly allow it.
$cacheControl = ['max-age', 's-maxage', 'must-revalidate', 'proxy-revalidate', 'public', 'private'];
foreach ($cacheControl as $key) {
if ($response->headers->hasCacheControlDirective($key)) {
return false;
}
}
if ($response->headers->has('Expires')) {
return false;
}
return true;
}
/**
* Store lowest max-age/s-maxage/expires for the final response.
*
* The response might have been stored in cache a while ago. To keep things comparable,
* we have to subtract the age so that the value is normalized for an age of 0.
*
* If the value is lower than the currently stored value, we update the value, to keep a rolling
* minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
*
* @param string $directive
* @param int|null $value
* @param int $age
*/
private function storeRelativeAgeDirective($directive, $value, $age)
{
if (null === $value) {
$this->ageDirectives[$directive] = false;
}
if (false !== $this->ageDirectives[$directive]) {
$value = $value - $age;
$this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
}
$response->setMaxAge(0);
}
}

View File

@ -237,4 +237,233 @@ class ResponseCacheStrategyTest extends TestCase
$this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage'));
$this->assertFalse($masterResponse->isValidateable());
}
/**
* @dataProvider cacheControlMergingProvider
*/
public function testCacheControlMerging(array $expects, array $master, array $surrogates)
{
$cacheStrategy = new ResponseCacheStrategy();
$buildResponse = function ($config) {
$response = new Response();
foreach ($config as $key => $value) {
switch ($key) {
case 'age':
$response->headers->set('Age', $value);
break;
case 'expires':
$expires = clone $response->getDate();
$expires->modify('+'.$value.' seconds');
$response->setExpires($expires);
break;
case 'max-age':
$response->setMaxAge($value);
break;
case 's-maxage':
$response->setSharedMaxAge($value);
break;
case 'private':
$response->setPrivate();
break;
case 'public':
$response->setPublic();
break;
default:
$response->headers->addCacheControlDirective($key, $value);
}
}
return $response;
};
foreach ($surrogates as $config) {
$cacheStrategy->add($buildResponse($config));
}
$response = $buildResponse($master);
$cacheStrategy->update($response);
foreach ($expects as $key => $value) {
if ('expires' === $key) {
$this->assertSame($value, $response->getExpires()->format('U') - $response->getDate()->format('U'));
} elseif ('age' === $key) {
$this->assertSame($value, $response->getAge());
} elseif (true === $value) {
$this->assertTrue($response->headers->hasCacheControlDirective($key), sprintf('Cache-Control header must have "%s" flag', $key));
} elseif (false === $value) {
$this->assertFalse(
$response->headers->hasCacheControlDirective($key),
sprintf('Cache-Control header must NOT have "%s" flag', $key)
);
} else {
$this->assertSame($value, $response->headers->getCacheControlDirective($key), sprintf('Cache-Control flag "%s" should be "%s"', $key, $value));
}
}
}
public function cacheControlMergingProvider()
{
yield 'result is public if all responses are public' => [
['private' => false, 'public' => true],
['public' => true],
[
['public' => true],
],
];
yield 'result is private by default' => [
['private' => true, 'public' => false],
['public' => true],
[
[],
],
];
yield 'combines public and private responses' => [
['must-revalidate' => false, 'private' => true, 'public' => false],
['public' => true],
[
['private' => true],
],
];
yield 'inherits no-cache from surrogates' => [
['no-cache' => true, 'public' => false],
['public' => true],
[
['no-cache' => true],
],
];
yield 'inherits no-store from surrogate' => [
['no-store' => true, 'public' => false],
['public' => true],
[
['no-store' => true],
],
];
yield 'resolve to lowest possible max-age' => [
['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
['public' => true, 'max-age' => 3600],
[
['private' => true, 'max-age' => 60],
],
];
yield 'resolves multiple max-age' => [
['public' => false, 'private' => true, 's-maxage' => false, 'max-age' => '60'],
['private' => true, 'max-age' => 100],
[
['private' => true, 'max-age' => 3600],
['public' => true, 'max-age' => 60, 's-maxage' => 60],
['private' => true, 'max-age' => 60],
],
];
yield 'merge max-age and s-maxage' => [
['public' => true, 's-maxage' => '60', 'max-age' => null],
['public' => true, 's-maxage' => 3600],
[
['public' => true, 'max-age' => 60],
],
];
yield 'result is private when combining private responses' => [
['no-cache' => false, 'must-revalidate' => false, 'private' => true],
['s-maxage' => 60, 'private' => true],
[
['s-maxage' => 60, 'private' => true],
],
];
yield 'result can have s-maxage and max-age' => [
['public' => true, 'private' => false, 's-maxage' => '60', 'max-age' => '30'],
['s-maxage' => 100, 'max-age' => 2000],
[
['s-maxage' => 1000, 'max-age' => 30],
['s-maxage' => 500, 'max-age' => 500],
['s-maxage' => 60, 'max-age' => 1000],
],
];
yield 'does not set headers without value' => [
['max-age' => null, 's-maxage' => null, 'public' => null],
['private' => true],
[
['private' => true],
],
];
yield 'max-age 0 is sent to the client' => [
['private' => true, 'max-age' => '0'],
['max-age' => 0, 'private' => true],
[
['max-age' => 60, 'private' => true],
],
];
yield 'max-age is relative to age' => [
['max-age' => '240', 'age' => 60],
['max-age' => 180],
[
['max-age' => 600, 'age' => 60],
],
];
yield 'retains lowest age of all responses' => [
['max-age' => '160', 'age' => 60],
['max-age' => 600, 'age' => 60],
[
['max-age' => 120, 'age' => 20],
],
];
yield 'max-age can be less than age, essentially expiring the response' => [
['age' => 120, 'max-age' => '90'],
['max-age' => 90, 'age' => 120],
[
['max-age' => 120, 'age' => 60],
],
];
yield 'max-age is 0 regardless of age' => [
['max-age' => '0'],
['max-age' => 60],
[
['max-age' => 0, 'age' => 60],
],
];
yield 'max-age is not negative' => [
['max-age' => '0'],
['max-age' => 0],
[
['max-age' => 0, 'age' => 60],
],
];
yield 'calculates lowest Expires header' => [
['expires' => 60],
['expires' => 60],
[
['expires' => 120],
],
];
yield 'calculates Expires header relative to age' => [
['expires' => 210, 'age' => 120],
['expires' => 90],
[
['expires' => 600, 'age' => '120'],
],
];
}
}

View File

@ -310,6 +310,30 @@
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Šī vērtība neatbilst sagaidāmajai rakstzīmju kopai {{ charset }}.</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>Šī vērtība nav derīgs Biznesa Identifikācijas Kods (BIC).</target>
</trans-unit>
<trans-unit id="82">
<source>Error</source>
<target>Kļūda</target>
</trans-unit>
<trans-unit id="83">
<source>This is not a valid UUID.</source>
<target>Šis nav derīgs UUID.</target>
</trans-unit>
<trans-unit id="84">
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Šai vērtībai jābūt vairākas reizes atkārtotai {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Šis Biznesa Identifikācijas Kods (BIC) neatbilst {{ iban }} konta numuram (IBAN).</target>
</trans-unit>
<trans-unit id="86">
<source>This value should be valid JSON.</source>
<target>Šai vērtībai jābūt derīgam JSON.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -4,39 +4,39 @@
<body>
<trans-unit id="1">
<source>This value should be false.</source>
<target>Vrednost treba da bude netačna.</target>
<target>Vrednost bi trebalo da bude netačna.</target>
</trans-unit>
<trans-unit id="2">
<source>This value should be true.</source>
<target>Vrednost treba da bude tačna.</target>
<target>Vrednost bi trebalo da bude tačna.</target>
</trans-unit>
<trans-unit id="3">
<source>This value should be of type {{ type }}.</source>
<target>Vrednost treba da bude tipa {{ type }}.</target>
<target>Vrednost bi trebalo da bude tipa {{ type }}.</target>
</trans-unit>
<trans-unit id="4">
<source>This value should be blank.</source>
<target>Vrednost treba da bude prazna.</target>
<target>Vrednost bi trebalo da bude prazna.</target>
</trans-unit>
<trans-unit id="5">
<source>The value you selected is not a valid choice.</source>
<target>Vrednost treba da bude jedna od ponuđenih.</target>
<target>Odabrana vrednost nije validan izbor.</target>
</trans-unit>
<trans-unit id="6">
<source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
<target>Izaberite bar {{ limit }} mogućnost.|Izaberite bar {{ limit }} mogućnosti.|Izaberite bar {{ limit }} mogućnosti.</target>
<target>Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.</target>
</trans-unit>
<trans-unit id="7">
<source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
<target>Izaberite najviše {{ limit }} mogućnost.|Izaberite najviše {{ limit }} mogućnosti.|Izaberite najviše {{ limit }} mogućnosti.</target>
<target>Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.</target>
</trans-unit>
<trans-unit id="8">
<source>One or more of the given values is invalid.</source>
<target>Jedna ili više vrednosti je nevalidna.</target>
<target>Jedna ili više vrednosti nisu validne.</target>
</trans-unit>
<trans-unit id="9">
<source>This field was not expected.</source>
<target>Ovo polje ne očekuje.</target>
<target>Ovo polje nije bilo očekivano.</target>
</trans-unit>
<trans-unit id="10">
<source>This field is missing.</source>
@ -48,7 +48,7 @@
</trans-unit>
<trans-unit id="12">
<source>This value is not a valid datetime.</source>
<target>Vrednost nije validan datum-vreme.</target>
<target>Vrednost nije validno vreme.</target>
</trans-unit>
<trans-unit id="13">
<source>This value is not a valid email address.</source>
@ -68,39 +68,39 @@
</trans-unit>
<trans-unit id="17">
<source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.</source>
<target>Mime tip datoteke nije validan ({{ type }}). Dozvoljeni mime tipovi su {{ types }}.</target>
<target>MIME tip datoteke nije validan ({{ type }}). Dozvoljeni MIME tipovi su {{ types }}.</target>
</trans-unit>
<trans-unit id="18">
<source>This value should be {{ limit }} or less.</source>
<target>Vrednost treba da bude {{ limit }} ili manje.</target>
<target>Vrednost bi trebalo da bude {{ limit }} ili manje.</target>
</trans-unit>
<trans-unit id="19">
<source>This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.</source>
<target>Vrednost je predugačka. Treba da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Treba da ima {{ limit }} karaktera ili manje.|Vrednost je predugačka. Treba da ima {{ limit }} karaktera ili manje.</target>
<target>Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.</target>
</trans-unit>
<trans-unit id="20">
<source>This value should be {{ limit }} or more.</source>
<target>Vrednost treba da bude {{ limit }} ili više.</target>
<target>Vrednost bi trebalo da bude {{ limit }} ili više.</target>
</trans-unit>
<trans-unit id="21">
<source>This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.</source>
<target>Vrednost je prekratka. Treba da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Treba da ima {{ limit }} karaktera ili više.|Vrednost je prekratka. Treba da ima {{ limit }} karaktera ili više.</target>
<target>Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.</target>
</trans-unit>
<trans-unit id="22">
<source>This value should not be blank.</source>
<target>Vrednost ne treba da bude prazna.</target>
<target>Vrednost ne bi trebalo da bude prazna.</target>
</trans-unit>
<trans-unit id="23">
<source>This value should not be null.</source>
<target>Vrednost ne treba da bude null.</target>
<target>Vrednost ne bi trebalo da bude prazna.</target>
</trans-unit>
<trans-unit id="24">
<source>This value should be null.</source>
<target>Vrednost treba da bude null.</target>
<target>Vrednost bi trebalo da bude prazna.</target>
</trans-unit>
<trans-unit id="25">
<source>This value is not valid.</source>
<target>Vrednost je nevalidna.</target>
<target>Vrednost nije validna.</target>
</trans-unit>
<trans-unit id="26">
<source>This value is not a valid time.</source>
@ -112,7 +112,7 @@
</trans-unit>
<trans-unit id="31">
<source>The two values should be equal.</source>
<target>Obe vrednosti treba da budu jednake.</target>
<target>Obe vrednosti bi trebalo da budu jednake.</target>
</trans-unit>
<trans-unit id="32">
<source>The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.</source>
@ -128,7 +128,7 @@
</trans-unit>
<trans-unit id="35">
<source>This value should be a valid number.</source>
<target>Vrednost treba da bude validan broj.</target>
<target>Vrednost bi trebalo da bude validan broj.</target>
</trans-unit>
<trans-unit id="36">
<source>This file is not a valid image.</source>
@ -144,11 +144,11 @@
</trans-unit>
<trans-unit id="39">
<source>This value is not a valid locale.</source>
<target>Vrednost nije validan lokal.</target>
<target>Vrednost nije validna međunarodna oznaka jezika.</target>
</trans-unit>
<trans-unit id="40">
<source>This value is not a valid country.</source>
<target>Vrednost nije validna zemlja.</target>
<target>Vrednost nije validna država.</target>
</trans-unit>
<trans-unit id="41">
<source>This value is already used.</source>
@ -160,27 +160,27 @@
</trans-unit>
<trans-unit id="43">
<source>The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.</source>
<target>Širina slike je prevelika ({{ width }}px). Najeća dozvoljena širina je {{ max_width }}px.</target>
<target>Širina slike je prevelika ({{ width }} piksela). Najveća dozvoljena širina je {{ max_width }} piksela.</target>
</trans-unit>
<trans-unit id="44">
<source>The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.</source>
<target>Širina slike je premala ({{ width }}px). Najmanja dozvoljena širina je {{ min_width }}px.</target>
<target>Širina slike je premala ({{ width }} piksela). Najmanja dozvoljena širina je {{ min_width }} piksela.</target>
</trans-unit>
<trans-unit id="45">
<source>The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.</source>
<target>Visina slike je prevelika ({{ height }}px). Najeća dozvoljena visina je {{ max_height }}px.</target>
<target>Visina slike je prevelika ({{ height }} piksela). Najveća dozvoljena visina je {{ max_height }} piksela.</target>
</trans-unit>
<trans-unit id="46">
<source>The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.</source>
<target>Visina slike je premala ({{ height }}px). Najmanja dozvoljena visina je {{ min_height }}px.</target>
<target>Visina slike je premala ({{ height }} piksela). Najmanja dozvoljena visina je {{ min_height }} piksela.</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user's current password.</source>
<target>Vrednost treba da bude trenutna korisnička lozinka.</target>
<target>Vrednost bi trebalo da bude trenutna korisnička lozinka.</target>
</trans-unit>
<trans-unit id="48">
<source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>
<target>Vrednost treba da ima tačno {{ limit }} karakter.|Vrednost treba da ima tačno {{ limit }} karaktera.|Vrednost treba da ima tačno {{ limit }} karaktera.</target>
<target>Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.</target>
</trans-unit>
<trans-unit id="49">
<source>The file was only partially uploaded.</source>
@ -204,23 +204,23 @@
</trans-unit>
<trans-unit id="54">
<source>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</source>
<target>Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili više elemenata.</target>
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.</target>
</trans-unit>
<trans-unit id="55">
<source>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</source>
<target>Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija treba da sadrži {{ limit }} ili manje elemenata.</target>
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
<target>Ova kolekcija treba da sadrži tačno {{ limit }} element.|Ova kolekcija treba da sadrži tačno {{ limit }} elementa.|Ova kolekcija treba da sadrži tačno {{ limit }} elemenata.</target>
<target>Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.</target>
</trans-unit>
<trans-unit id="57">
<source>Invalid card number.</source>
<target>Nevalidan broj kartice.</target>
<target>Broj kartice nije validan.</target>
</trans-unit>
<trans-unit id="58">
<source>Unsupported card type or invalid card number.</source>
<target>Nevalidan broj kartice ili tip kartice nije podržan.</target>
<target>Tip kartije nije podržan ili broj kartice nije validan.</target>
</trans-unit>
<trans-unit id="59">
<source>This is not a valid International Bank Account Number (IBAN).</source>
@ -248,35 +248,35 @@
</trans-unit>
<trans-unit id="65">
<source>This value should be equal to {{ compared_value }}.</source>
<target>Ova vrednost treba da bude {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude jednaka {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="66">
<source>This value should be greater than {{ compared_value }}.</source>
<target>Ova vrednost treba da bude veća od {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude veća od {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="67">
<source>This value should be greater than or equal to {{ compared_value }}.</source>
<target>Ova vrednost treba da bude veća ili jednaka {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude veća ili jednaka {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="68">
<source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Ova vrednost treba da bude identična sa {{ compared_value_type }} {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude identična sa {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="69">
<source>This value should be less than {{ compared_value }}.</source>
<target>Ova vrednost treba da bude manja od {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude manja od {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="70">
<source>This value should be less than or equal to {{ compared_value }}.</source>
<target>Ova vrednost treba da bude manja ili jednaka {{ compared_value }}.</target>
<target>Ova vrednost bi trebalo da bude manja ili jednaka {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="71">
<source>This value should not be equal to {{ compared_value }}.</source>
<target>Ova vrednost ne treba da bude jednaka {{ compared_value }}.</target>
<target>Ova vrednost ne bi trebalo da bude jednaka {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="72">
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>Ova vrednost ne treba da bude identična sa {{ compared_value_type }} {{ compared_value }}.</target>
<target>Ova vrednost ne bi trebalo da bude identična sa {{ compared_value_type }} {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="73">
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
@ -288,15 +288,51 @@
</trans-unit>
<trans-unit id="75">
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
<target>Slika je kvadratna ({{ width }}x{{ height }}px). Kvadratne slike nisu dozvoljene.</target>
<target>Slika je kvadratna ({{ width }}x{{ height }} piksela). Kvadratne slike nisu dozvoljene.</target>
</trans-unit>
<trans-unit id="76">
<source>The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.</source>
<target>Slika je orijentacije pejzaža ({{ width }}x{{ height }}px). Pejzažna orijentacija slika nije dozvoljena.</target>
<target>Slika je pejzažno orijentisana ({{ width }}x{{ height }} piksela). Pejzažna orijentisane slike nisu dozvoljene.</target>
</trans-unit>
<trans-unit id="77">
<source>The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.</source>
<target>Slika je orijantacije portreta ({{ width }}x{{ height }}px). Portretna orijentacija slika nije dozvoljena.</target>
<target>Slika je portretno orijentisana ({{ width }}x{{ height }} piksela). Portretno orijentisane slike nisu dozvoljene.</target>
</trans-unit>
<trans-unit id="78">
<source>An empty file is not allowed.</source>
<target>Prazna datoteka nije dozvoljena.</target>
</trans-unit>
<trans-unit id="79">
<source>The host could not be resolved.</source>
<target>Nije moguće odrediti poslužitelja.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>Vrednost se ne poklapa sa očekivanim {{ charset }} setom karaktera.</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>Ovo nije validan BIC.</target>
</trans-unit>
<trans-unit id="82">
<source>Error</source>
<target>Greška</target>
</trans-unit>
<trans-unit id="83">
<source>This is not a valid UUID.</source>
<target>Ovo nije validan univerzalni unikatni identifikator (UUID).</target>
</trans-unit>
<trans-unit id="84">
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Ova vrednost bi trebalo da bude višestruko veća od {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>BIC kod nije povezan sa IBAN {{ iban }}.</target>
</trans-unit>
<trans-unit id="86">
<source>This value should be valid JSON.</source>
<target>Ova vrednost bi trebalo da bude validan JSON.</target>
</trans-unit>
</body>
</file>