PluralTranslatableString.php 5.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
<?php

/**
 * @file
 * Contains \Drupal\Core\StringTranslation\PluralTranslatableString.
 */

namespace Drupal\Core\StringTranslation;

use Drupal\Component\Utility\PlaceholderTrait;

/**
 * A class to hold plural translatable strings.
 */
class PluralTranslatableString extends TranslatableString {
  use PlaceholderTrait;

  /**
   * The delimiter used to split plural strings.
   *
   * This is the ETX (End of text) character and is used as a minimal means to
   * separate singular and plural variants in source and translation text. It
   * was found to be the most compatible delimiter for the supported databases.
   */
  const DELIMITER = "\03";

  /**
   * The item count to display.
   *
   * @var int
   */
  protected $count;

  /**
   * The already translated string.
   *
   * @var string
   */
  protected $translatedString;

  /**
   * A bool that statically caches whether locale_get_plural() exists.
   *
   * @var bool
   */
  protected static $localeEnabled;

  /**
   * Constructs a new PluralTranslatableString object.
   *
   * Parses values passed into this class through the format_plural() function
   * in Drupal and handles an optional context for the string.
   *
   * @param int $count
   *   The item count to display.
   * @param string $singular
   *   The string for the singular case. Make sure it is clear this is singular,
   *   to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
   *   use @count in the singular string.
   * @param string $plural
   *   The string for the plural case. Make sure it is clear this is plural, to
   *   ease translation. Use @count in place of the item count, as in
   *   "@count new comments".
   * @param array $args
65 66 67 68
   *   (optional) An array with placeholder replacements, keyed by placeholder.
   *   See \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
   *   additional information about placeholders. Note that you do not need to
   *   include @count in this array; this replacement is done automatically
69 70 71 72 73 74
   *   for the plural cases.
   * @param array $options
   *   (optional) An associative array of additional options. See t() for
   *   allowed keys.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   (optional) The string translation service.
75 76
   *
   * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
   */
  public function __construct($count, $singular, $plural, array $args = [], array $options = [], TranslationInterface $string_translation = NULL) {
    $this->count = $count;
    $translatable_string = implode(static::DELIMITER, array($singular, $plural));
    parent::__construct($translatable_string, $args, $options, $string_translation);
  }

  /**
   * Constructs a new class instance from an already translated string.
   *
   * This method ensures that the string is pluralized correctly. As opposed
   * to the __construct() method, this method is designed to be invoked with
   * a string already translated (such as with configuration translation).
   *
   * @param int $count
   *   The item count to display.
   * @param string $translated_string
   *   The already translated string.
   * @param array $args
   *   An associative array of replacements to make after translation. Instances
   *   of any key in this array are replaced with the corresponding value.
   *   Based on the first character of the key, the value is escaped and/or
   *   themed. See \Drupal\Component\Utility\SafeMarkup::format(). Note that you
   *   do not need to include @count in this array; this replacement is done
   *   automatically for the plural cases.
   * @param array $options
   *   An associative array of additional options. See t() for allowed keys.
   *
   * @return \Drupal\Core\StringTranslation\PluralTranslatableString
   *   A PluralTranslatableString object.
   */
  public static function createFromTranslatedString($count, $translated_string, array $args = [], array $options = []) {
    $plural = new static($count, '', '', $args, $options);
    $plural->translatedString = $translated_string;
111
    return $plural;
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  }

  /**
   * Renders the object as a string.
   *
   * @return string
   *   The translated string.
   */
  public function render() {
    if (!$this->translatedString) {
      $this->translatedString = $this->getStringTranslation()->translateString($this);
    }
    if ($this->translatedString === '') {
      return '';
    }

    $arguments = $this->getArguments();
    $arguments['@count'] = $this->count;
    $translated_array = explode(static::DELIMITER, $this->translatedString);

    if ($this->count == 1) {
      return $this->placeholderFormat($translated_array[0], $arguments);
    }

    $index = $this->getPluralIndex();
    if ($index == 0) {
      // Singular form.
      $return = $translated_array[0];
    }
    else {
      if (isset($translated_array[$index])) {
        // N-th plural form.
        $return = $translated_array[$index];
      }
      else {
        // If the index cannot be computed or there's no translation, use the
        // second plural form as a fallback (which allows for most flexibility
        // with the replaceable @count value).
        $return = $translated_array[1];
      }
    }

    return $this->placeholderFormat($return, $arguments);
  }

  /**
   * Gets the plural index through the gettext formula.
   *
   * @return int
   */
  protected function getPluralIndex() {
    if (!isset(static::$localeEnabled)) {
      static::$localeEnabled = function_exists('locale_get_plural');
    }
    if (function_exists('locale_get_plural')) {
      return locale_get_plural($this->count, $this->getOption('langcode'));
    }
    return -1;
  }

}