TranslationExtension.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bridge\Twig\Extension;
  11. use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
  12. use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
  13. use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
  14. use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
  15. use Symfony\Component\Translation\TranslatableMessage;
  16. use Symfony\Contracts\Translation\TranslatableInterface;
  17. use Symfony\Contracts\Translation\TranslatorInterface;
  18. use Symfony\Contracts\Translation\TranslatorTrait;
  19. use Twig\Extension\AbstractExtension;
  20. use Twig\TwigFilter;
  21. use Twig\TwigFunction;
  22. // Help opcache.preload discover always-needed symbols
  23. class_exists(TranslatorInterface::class);
  24. class_exists(TranslatorTrait::class);
  25. /**
  26. * Provides integration of the Translation component with Twig.
  27. *
  28. * @author Fabien Potencier <fabien@symfony.com>
  29. */
  30. final class TranslationExtension extends AbstractExtension
  31. {
  32. private $translator;
  33. private $translationNodeVisitor;
  34. public function __construct(TranslatorInterface $translator = null, TranslationNodeVisitor $translationNodeVisitor = null)
  35. {
  36. $this->translator = $translator;
  37. $this->translationNodeVisitor = $translationNodeVisitor;
  38. }
  39. public function getTranslator(): TranslatorInterface
  40. {
  41. if (null === $this->translator) {
  42. if (!interface_exists(TranslatorInterface::class)) {
  43. throw new \LogicException(sprintf('You cannot use the "%s" if the Translation Contracts are not available. Try running "composer require symfony/translation".', __CLASS__));
  44. }
  45. $this->translator = new class() implements TranslatorInterface {
  46. use TranslatorTrait;
  47. };
  48. }
  49. return $this->translator;
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function getFunctions(): array
  55. {
  56. return [
  57. new TwigFunction('t', [$this, 'createTranslatable']),
  58. ];
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function getFilters(): array
  64. {
  65. return [
  66. new TwigFilter('trans', [$this, 'trans']),
  67. ];
  68. }
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public function getTokenParsers(): array
  73. {
  74. return [
  75. // {% trans %}Symfony is great!{% endtrans %}
  76. new TransTokenParser(),
  77. // {% trans_default_domain "foobar" %}
  78. new TransDefaultDomainTokenParser(),
  79. ];
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. public function getNodeVisitors(): array
  85. {
  86. return [$this->getTranslationNodeVisitor(), new TranslationDefaultDomainNodeVisitor()];
  87. }
  88. public function getTranslationNodeVisitor(): TranslationNodeVisitor
  89. {
  90. return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor();
  91. }
  92. /**
  93. * @param string|\Stringable|TranslatableInterface|null $message
  94. * @param array|string $arguments Can be the locale as a string when $message is a TranslatableInterface
  95. */
  96. public function trans($message, $arguments = [], string $domain = null, string $locale = null, int $count = null): string
  97. {
  98. if ($message instanceof TranslatableInterface) {
  99. if ([] !== $arguments && !\is_string($arguments)) {
  100. throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments)));
  101. }
  102. return $message->trans($this->getTranslator(), $locale ?? (\is_string($arguments) ? $arguments : null));
  103. }
  104. if (!\is_array($arguments)) {
  105. throw new \TypeError(sprintf('Unless the message is a "%s", argument 2 passed to "%s()" must be an array of parameters, "%s" given.', TranslatableInterface::class, __METHOD__, get_debug_type($arguments)));
  106. }
  107. if ('' === $message = (string) $message) {
  108. return '';
  109. }
  110. if (null !== $count) {
  111. $arguments['%count%'] = $count;
  112. }
  113. return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
  114. }
  115. public function createTranslatable(string $message, array $parameters = [], string $domain = null): TranslatableMessage
  116. {
  117. if (!class_exists(TranslatableMessage::class)) {
  118. throw new \LogicException(sprintf('You cannot use the "%s" as the Translation Component is not installed. Try running "composer require symfony/translation".', __CLASS__));
  119. }
  120. return new TranslatableMessage($message, $parameters, $domain);
  121. }
  122. }