[Form] Add intl/choice_translation_locale option to TimezoneType
This commit is contained in:
parent
9e55e9c982
commit
001b930611
@ -13,10 +13,12 @@ namespace Symfony\Component\Form\Extension\Core\Type;
|
|||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
|
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
|
||||||
|
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
|
||||||
use Symfony\Component\Form\Exception\LogicException;
|
use Symfony\Component\Form\Exception\LogicException;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntlTimeZoneToStringTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\IntlTimeZoneToStringTransformer;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Intl\Timezones;
|
||||||
use Symfony\Component\OptionsResolver\Options;
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
@ -40,19 +42,41 @@ class TimezoneType extends AbstractType
|
|||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
|
'intl' => false,
|
||||||
'choice_loader' => function (Options $options) {
|
'choice_loader' => function (Options $options) {
|
||||||
$regions = $options->offsetGet('regions', false);
|
|
||||||
$input = $options['input'];
|
$input = $options['input'];
|
||||||
|
|
||||||
|
if ($options['intl']) {
|
||||||
|
$choiceTranslationLocale = $options['choice_translation_locale'];
|
||||||
|
|
||||||
|
return new IntlCallbackChoiceLoader(function () use ($input, $choiceTranslationLocale) {
|
||||||
|
return self::getIntlTimezones($input, $choiceTranslationLocale);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$regions = $options->offsetGet('regions', false);
|
||||||
|
|
||||||
return new CallbackChoiceLoader(function () use ($regions, $input) {
|
return new CallbackChoiceLoader(function () use ($regions, $input) {
|
||||||
return self::getTimezones($regions, $input);
|
return self::getPhpTimezones($regions, $input);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'choice_translation_domain' => false,
|
'choice_translation_domain' => false,
|
||||||
|
'choice_translation_locale' => null,
|
||||||
'input' => 'string',
|
'input' => 'string',
|
||||||
'regions' => \DateTimeZone::ALL,
|
'regions' => \DateTimeZone::ALL,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('intl', ['bool']);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
|
||||||
|
$resolver->setNormalizer('choice_translation_locale', function (Options $options, $value) {
|
||||||
|
if (null !== $value && !$options['intl']) {
|
||||||
|
throw new LogicException('The "choice_translation_locale" option can only be used if the "intl" option is set to true.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
});
|
||||||
|
|
||||||
$resolver->setAllowedValues('input', ['string', 'datetimezone', 'intltimezone']);
|
$resolver->setAllowedValues('input', ['string', 'datetimezone', 'intltimezone']);
|
||||||
$resolver->setNormalizer('input', function (Options $options, $value) {
|
$resolver->setNormalizer('input', function (Options $options, $value) {
|
||||||
if ('intltimezone' === $value && !class_exists(\IntlTimeZone::class)) {
|
if ('intltimezone' === $value && !class_exists(\IntlTimeZone::class)) {
|
||||||
@ -64,6 +88,13 @@ class TimezoneType extends AbstractType
|
|||||||
|
|
||||||
$resolver->setAllowedTypes('regions', 'int');
|
$resolver->setAllowedTypes('regions', 'int');
|
||||||
$resolver->setDeprecated('regions', 'The option "%name%" is deprecated since Symfony 4.2.');
|
$resolver->setDeprecated('regions', 'The option "%name%" is deprecated since Symfony 4.2.');
|
||||||
|
$resolver->setNormalizer('regions', function (Options $options, $value) {
|
||||||
|
if ($options['intl'] && \DateTimeZone::ALL !== (\DateTimeZone::ALL & $value)) {
|
||||||
|
throw new LogicException('The "regions" option can only be used if the "intl" option is set to false.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,10 +113,7 @@ class TimezoneType extends AbstractType
|
|||||||
return 'timezone';
|
return 'timezone';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static function getPhpTimezones(int $regions, string $input): array
|
||||||
* Returns a normalized array of timezone choices.
|
|
||||||
*/
|
|
||||||
private static function getTimezones(int $regions, string $input): array
|
|
||||||
{
|
{
|
||||||
$timezones = [];
|
$timezones = [];
|
||||||
|
|
||||||
@ -99,4 +127,19 @@ class TimezoneType extends AbstractType
|
|||||||
|
|
||||||
return $timezones;
|
return $timezones;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function getIntlTimezones(string $input, string $locale = null): array
|
||||||
|
{
|
||||||
|
$timezones = array_flip(Timezones::getNames($locale));
|
||||||
|
|
||||||
|
if ('intltimezone' === $input) {
|
||||||
|
foreach ($timezones as $name => $timezone) {
|
||||||
|
if ('Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
|
||||||
|
unset($timezones[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $timezones;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
|
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
|
||||||
|
|
||||||
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
|
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
|
||||||
|
use Symfony\Component\Intl\Util\IntlTestHelper;
|
||||||
|
|
||||||
class TimezoneTypeTest extends BaseTypeTest
|
class TimezoneTypeTest extends BaseTypeTest
|
||||||
{
|
{
|
||||||
@ -83,6 +84,17 @@ class TimezoneTypeTest extends BaseTypeTest
|
|||||||
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Europe / Amsterdam'), $choices, '', false, false);
|
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Europe / Amsterdam'), $choices, '', false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
* @expectedDeprecation The option "regions" is deprecated since Symfony 4.2.
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\LogicException
|
||||||
|
* @expectedExceptionMessage The "regions" option can only be used if the "intl" option is set to false.
|
||||||
|
*/
|
||||||
|
public function testFilterByRegionsWithIntl()
|
||||||
|
{
|
||||||
|
$this->factory->create(static::TESTED_TYPE, null, ['regions' => \DateTimeZone::EUROPE, 'intl' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @requires extension intl
|
* @requires extension intl
|
||||||
*/
|
*/
|
||||||
@ -116,4 +128,54 @@ class TimezoneTypeTest extends BaseTypeTest
|
|||||||
$this->assertNull($form->getData());
|
$this->assertNull($form->getData());
|
||||||
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
|
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires extension intl
|
||||||
|
*/
|
||||||
|
public function testIntlTimeZoneInputWithBcAndIntl()
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(static::TESTED_TYPE, null, ['input' => 'intltimezone', 'intl' => true]);
|
||||||
|
$form->submit('Europe/Saratov');
|
||||||
|
|
||||||
|
$this->assertNull($form->getData());
|
||||||
|
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTimezonesAreSelectableWithIntl()
|
||||||
|
{
|
||||||
|
IntlTestHelper::requireIntl($this, false);
|
||||||
|
|
||||||
|
$choices = $this->factory->create(static::TESTED_TYPE, null, ['intl' => true])
|
||||||
|
->createView()->vars['choices'];
|
||||||
|
|
||||||
|
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Central European Time (Amsterdam)'), $choices, '', false, false);
|
||||||
|
$this->assertContains(new ChoiceView('Etc/UTC', 'Etc/UTC', 'Coordinated Universal Time'), $choices, '', false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires extension intl
|
||||||
|
*/
|
||||||
|
public function testChoiceTranslationLocaleOptionWithIntl()
|
||||||
|
{
|
||||||
|
$choices = $this->factory
|
||||||
|
->create(static::TESTED_TYPE, null, [
|
||||||
|
'intl' => true,
|
||||||
|
'choice_translation_locale' => 'uk',
|
||||||
|
])
|
||||||
|
->createView()->vars['choices'];
|
||||||
|
|
||||||
|
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'за центральноєвропейським часом (Амстердам)'), $choices, '', false, false);
|
||||||
|
$this->assertContains(new ChoiceView('Etc/UTC', 'Etc/UTC', 'за всесвітнім координованим часом'), $choices, '', false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\LogicException
|
||||||
|
* @expectedExceptionMessage The "choice_translation_locale" option can only be used if the "intl" option is set to true.
|
||||||
|
*/
|
||||||
|
public function testChoiceTranslationLocaleOptionWithoutIntl()
|
||||||
|
{
|
||||||
|
$this->factory->create(static::TESTED_TYPE, null, [
|
||||||
|
'choice_translation_locale' => 'uk',
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user