mustache_templates-8.x-1.0-beta4/src/Plugin/mustache/Magic/Translation.php

src/Plugin/mustache/Magic/Translation.php
<?php

namespace Drupal\mustache\Plugin\mustache\Magic;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\mustache\Element\Mustache;
use Drupal\mustache\Helpers\MustacheRenderTemplate;
use Drupal\mustache\Plugin\MustacheMagic;
use Drupal\mustache\Render\Markup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Pass through template contents to Drupal's string translation system.
 *
 * Usage:
 *
 * You can use the "magical" {{#t.<langcode>}} section variable to pass template
 * contents to Drupal's translation system. You can specify an ISO 639-1
 * language code as target translation or use either one of "i", "interface" and
 * "current" to use the current interface language, for example {{#t.current}}.
 *
 * @code
 * {{#t.i}}I am an english text and will be substituted by an according translation of the current interface language.{{/t.i}}
 * {{#t.de}}I am an english text and will be substituted by an according German translation.{{/t.de}}
 * @endcode
 *
 * @MustacheMagic(
 *   id = "t",
 *   label = @Translation("Translation"),
 *   description = @Translation("Use <b>{{#t.&lt;langcode&gt;}}</b> to pass template contents to Drupal's translation system. You can specify an ISO 639-1 language code as target translation or use either &quot;i&quot;, &quot;interface&quot; or &quot;current&quot; to use the current language. Examples: <ul><li>{{#t.i}}I am an english text and will be substituted by an according translation of the current interface language.{{/t.i}}</li><li>{{#t.de}}I am an english text and will be substituted by an according German translation.{{/t.de}}</li></ul>")
 * )
 */
class Translation extends MustacheMagic {

  use StringTranslationTrait;

  /**
   * Whether this object is a clone.
   *
   * @var bool
   */
  protected $cloned = FALSE;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * The langcode to use.
   *
   * @var string|null
   */
  protected $langcode;

  /**
   * The renderer.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * The client library for magic translations.
   *
   * @var string
   */
  public static $library = 'mustache/magic.translation';

  /**
   * A list of reserved keywords that may be used as synonym for UI language.
   *
   * @var array
   */
  public static $interfaceLanguageKeywords = ['i', 'interface', 'current'];

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->languageManager = $container->get('language_manager');
    $instance->stringTranslation = $container->get('string_translation');
    $instance->renderer = $container->get('renderer');
    return $instance;
  }

  /**
   * Implementation of the magic __isset() method.
   */
  public function __isset($name): bool {
    return !empty($name) && (strlen($name) === 2 || in_array(mb_strtolower(trim($name)), static::$interfaceLanguageKeywords));
  }

  /**
   * Implementation of the magic __get() method.
   */
  public function __get($name) {
    if (!$this->cloned) {
      $cloned = clone $this;
      $cloned->cloned = TRUE;
      return $cloned->__get($name);
    }
    $name = mb_strtolower(trim($name));
    $this->langcode = $name;
    return $this;
  }

  /**
   * Implements the magic __invoke() method, used as higher-order section.
   */
  public function __invoke($template_content = NULL, $render = NULL) {
    if (!isset($template_content, $render)) {
      return;
    }

    // Attach the library at the beginning, so that BubbleableMetadata will
    // include that one (and will not forget about it).
    $this->attachLibrary();

    if (!empty($this->langcode) && !in_array($this->langcode, static::$interfaceLanguageKeywords) && !$this->languageManager->getLanguage($this->langcode)) {
      // Invalid or nonexistent langcode specified.
      $this->langcode = NULL;
    }
    $current_langcode = $this->languageManager->getCurrentLanguage()->getId();
    $langcode = $this->langcode ?? $current_langcode;
    if (in_array($langcode, static::$interfaceLanguageKeywords)) {
      $langcode = $current_langcode;
    }
    $language = $this->languageManager->getLanguage($langcode);

    if (!$language) {
      // Nothing to do when language does not exist for the given langcode.
      return $render($template_content);
    }

    if (isset($this->langcode) && !empty($this->element['#use_sync'])) {
      $this->element['#attached']['drupalSettings']['mustache']['lang'][$this->langcode] = $langcode;
    }

    $requires_isolated_render = FALSE;
    $translated_content = (string) $this->t($template_content, [], [
      'langcode' => $langcode,
    ]);
    $template_name = $langcode . '-' . hash('md4', $translated_content);
    $bubbleable_metadata = BubbleableMetadata::createFromRenderArray($this->element);

    if (!$this->isInline() && \Drupal::hasService('mustache.template_storage')) {
      /** @var \Drupal\mustache_magic\Storage\MustacheTemplateStorage $template_storage */
      $template_storage = \Drupal::service('mustache.template_storage');
      $template_storage->registerTemplate($template_name, $translated_content);
      $build = MustacheRenderTemplate::build($template_name);
    }
    else {
      $build = MustacheRenderTemplate::build($template_name, $translated_content);
    }
    $build_render_array = &$build->toRenderArray();

    $data_keys = [
      ['#data'],
      ['#override_data'],
      ['#with_tokens', 'data'],
    ];
    if (!empty($this->element['#sync']['items'])) {
      foreach ($this->element['#sync']['items'] as $sync_i => &$sync_item) {
        if (isset($sync_item['data'])) {
          $data_keys[] = ['#sync', 'items', $sync_i, 'data'];
        }
        if (isset($sync_item['url'])) {
          $data_keys[] = ['#sync', 'items', $sync_i, 'url'];
        }
      }
    }
    foreach ($data_keys as $data_key) {
      $key_exists = FALSE;
      $data = NestedArray::getValue($this->element, $data_key, $key_exists);
      if ($key_exists) {
        if ($data_key[0] === '#sync' && $data_key[1] === 'items' && is_string($data)) {
          $data = json_decode($data, TRUE);
        }
        if (is_array($data)) {
          foreach ($data as $key => $value) {
            if ($value instanceof TranslatableInterface) {
              if ($value->language()->getId() !== $langcode && $value->hasTranslation($langcode)) {
                $value = $value->getTranslation($langcode);
                if ($value instanceof AccessibleInterface && !$value->access('view')) {
                  continue;
                }
                if ($value) {
                  $requires_isolated_render = TRUE;
                  $data[$key] = $value;
                }
              }
            }
            elseif ($value instanceof TranslatableMarkup) {
              $options = $value->getOptions();
              if ((!isset($options['langcode']) && ($current_langcode !== $langcode)) || (isset($options['langcode']) && $options['langcode'] !== $langcode)) {
                $requires_isolated_render = TRUE;
                $options['langcode'] = $langcode;
              }
              $data[$key] = $this->t($value->getUntranslatedString(), $value->getArguments(), $options);
            }
            elseif (is_string($value)) {
              $data[$key] = (string) $this->t($value, [], [
                'langcode' => $langcode,
              ]);
              if ($value !== $data[$key]) {
                $requires_isolated_render = TRUE;
              }
            }
          }
          NestedArray::setValue($build_render_array, $data_key, $data, TRUE);
        }
        elseif ($url = Mustache::getUrlFromParam($data)) {
          $url_string = $url->toString();
          $query_params = $url->getOption('query');
          $url_lang = $url->getOption('language');
          $path_lang = NULL;
          if ($url_path = parse_url($url_string, PHP_URL_PATH)) {
            if (substr($url_path, 0, 1) == '/') {
              $url_path = substr($url_path, 1);
            }
            $url_path_parts = explode('/', $url_path);
            $path_arg = (string) array_shift($url_path_parts);
            $path_lang = !empty($path_arg) ? $this->languageManager->getLanguage($path_arg) : NULL;
          }
          $lang_set = $url_lang || !empty($query_params['langcode']) || $path_lang;
          $lang_equal_or_empty = !$lang_set || ($url_lang && $url_lang->getId() == $langcode) || ($path_lang && $path_lang->getId() == $langcode) || (isset($query_params['langcode']) && $query_params['langcode'] == $langcode);
          if ($lang_equal_or_empty && (!$url->isExternal() || $url->isRouted())) {
            $url->setOption('language', $language);
          }
          if ($path_lang) {
            array_unshift($url_path_parts, $langcode);
            $url_string = str_replace($url_path, implode('/', $url_path_parts), $url_string);
            $url = Mustache::getUrlFromParam($url_string);
          }
          if (!$lang_equal_or_empty) {
            $requires_isolated_render = TRUE;
            $url = clone $url;
          }
          if (!$url->isExternal() || $url->isRouted()) {
            $url->setOption('language', $language);
            $lang_set = TRUE;
          }
          if (!$lang_set || isset($query_params['langcode'])) {
            $url = Mustache::rebuildUrl($url, ['langcode' => $langcode]);
          }
          NestedArray::setValue($build_render_array, $data_key, $url, TRUE);
          if ($data_key[0] === '#sync') {
            if ($lang_equal_or_empty && !$requires_isolated_render) {
              $url->setAbsolute($url->isExternal());
              NestedArray::setValue($this->element, $data_key, json_encode($url->toString(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_FORCE_OBJECT), TRUE);
              unset($build_render_array['#sync']['items'][$data_key[2]]);
            }
            else {
              $wrapper_selector = NULL;
              if (!empty($this->element['#sync']['wrapper_tag']) && isset($this->element['#sync']['attributes']['id'])) {
                $wrapper_selector = '#' . $this->element['#sync']['attributes']['id'];
              }
              elseif (!empty($this->element['#sync']['into'])) {
                $wrapper_selector = $this->element['#sync']['into'];
              }
              if ($wrapper_selector && $wrapper_selector !== '#') {
                $build_render_array['#sync']['items'][$data_key[2]]['trigger'] = [
                  [$wrapper_selector, 'mustacheSyncBegin', -1],
                ];
              }
              else {
                foreach (['delay', 'period', 'limit', 'trigger'] as $sync_setting) {
                  if (isset($this->element['#sync']['items'][$data_key[2]][$sync_setting])) {
                    $build_render_array['#sync']['items'][$data_key[2]][$sync_setting] = $this->element['#sync']['items'][$data_key[2]][$sync_setting];
                  }
                }
              }
            }
          }
        }
      }
    }
    if (empty($build_render_array['#sync']['items'])) {
      unset($build_render_array['#sync']);
    }
    if ($this->withTokens() && ((!isset($this->element['#with_tokens']['options']['langcode']) && ($current_langcode !== $langcode)) || (isset($this->element['#with_tokens']['options']['langcode']) && $this->element['#with_tokens']['options']['langcode'] !== $langcode))) {
      $requires_isolated_render = TRUE;
      $build_render_array['#with_tokens']['options']['langcode'] = $langcode;
    }

    if ($requires_isolated_render) {
      $build_render_array['#cache']['contexts'][] = 'languages';
      if (!empty($build_render_array['#sync'])) {
        $build_render_array['#sync']['wrapper_tag'] = NULL;
        unset($build_render_array['#sync']['into']);

        // Provide a mapping from untranslated to translated template contents.
        // The client library of this plugin then takes care to do the math.
        $trans_key = "trans-${current_langcode}-${template_name}";
        if (isset($template_storage)) {
          $template_storage->registerTemplate($trans_key, $template_content);
          $this->element['#partials'][$trans_key] = $trans_key;
        }
        else {
          $this->element['#inline_partials'][$trans_key] = [
            '#type' => 'mustache_template_js_inline',
            '#template' => $trans_key,
            '#inline' => $template_content,
          ];
        }
        // Prevent double-includes of partials, as this is a nested invokation.
        $build_render_array['#skip_partials'] = TRUE;
      }
      $renderer = $this->renderer;
      $rendered = $renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, &$build_render_array) {
        return $renderer->render($build_render_array);
      });
      $bubbleable_metadata
        ->addCacheableDependency(BubbleableMetadata::createFromRenderArray($build_render_array))
        ->applyTo($this->element);
      // We need to replace the opening curly brackets, so that Mustache.php's
      // lambda helper cannot chime in and would try to replace Mustache
      // variables within the inline template with current context values.
      return Markup::create(str_replace('{{', '\{\{', (string) $rendered));
    }
    else {
      $bubbleable_metadata->addCacheContexts(['languages'])->applyTo($this->element);
      return $render($translated_content);
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc