Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Core\StringTranslation;
|
Chris@0
|
4
|
Chris@18
|
5 use Drupal\Component\Gettext\PoItem;
|
Chris@18
|
6
|
Chris@0
|
7 /**
|
Chris@0
|
8 * A class to hold plural translatable markup.
|
Chris@0
|
9 */
|
Chris@0
|
10 class PluralTranslatableMarkup extends TranslatableMarkup {
|
Chris@0
|
11
|
Chris@0
|
12 /**
|
Chris@0
|
13 * The delimiter used to split plural strings.
|
Chris@0
|
14 *
|
Chris@0
|
15 * This is the ETX (End of text) character and is used as a minimal means to
|
Chris@0
|
16 * separate singular and plural variants in source and translation text. It
|
Chris@0
|
17 * was found to be the most compatible delimiter for the supported databases.
|
Chris@18
|
18 *
|
Chris@18
|
19 * @deprecated in Drupal 8.7.x, will be removed before Drupal 9.0.0.
|
Chris@18
|
20 * Use Drupal\Component\Gettext\PoItem::DELIMITER instead.
|
Chris@0
|
21 */
|
Chris@18
|
22 const DELIMITER = PoItem::DELIMITER;
|
Chris@0
|
23
|
Chris@0
|
24 /**
|
Chris@0
|
25 * The item count to display.
|
Chris@0
|
26 *
|
Chris@0
|
27 * @var int
|
Chris@0
|
28 */
|
Chris@0
|
29 protected $count;
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * The already translated string.
|
Chris@0
|
33 *
|
Chris@0
|
34 * @var string
|
Chris@0
|
35 */
|
Chris@0
|
36 protected $translatedString;
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * Constructs a new PluralTranslatableMarkup object.
|
Chris@0
|
40 *
|
Chris@0
|
41 * Parses values passed into this class through the format_plural() function
|
Chris@0
|
42 * in Drupal and handles an optional context for the string.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @param int $count
|
Chris@0
|
45 * The item count to display.
|
Chris@0
|
46 * @param string $singular
|
Chris@0
|
47 * The string for the singular case. Make sure it is clear this is singular,
|
Chris@0
|
48 * to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
|
Chris@0
|
49 * use @count in the singular string.
|
Chris@0
|
50 * @param string $plural
|
Chris@0
|
51 * The string for the plural case. Make sure it is clear this is plural, to
|
Chris@0
|
52 * ease translation. Use @count in place of the item count, as in
|
Chris@0
|
53 * "@count new comments".
|
Chris@0
|
54 * @param array $args
|
Chris@0
|
55 * (optional) An array with placeholder replacements, keyed by placeholder.
|
Chris@0
|
56 * See \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
|
Chris@0
|
57 * additional information about placeholders. Note that you do not need to
|
Chris@0
|
58 * include @count in this array; this replacement is done automatically
|
Chris@0
|
59 * for the plural cases.
|
Chris@0
|
60 * @param array $options
|
Chris@0
|
61 * (optional) An associative array of additional options. See t() for
|
Chris@0
|
62 * allowed keys.
|
Chris@0
|
63 * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
Chris@0
|
64 * (optional) The string translation service.
|
Chris@0
|
65 *
|
Chris@0
|
66 * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
|
Chris@0
|
67 */
|
Chris@0
|
68 public function __construct($count, $singular, $plural, array $args = [], array $options = [], TranslationInterface $string_translation = NULL) {
|
Chris@0
|
69 $this->count = $count;
|
Chris@18
|
70 $translatable_string = implode(PoItem::DELIMITER, [$singular, $plural]);
|
Chris@0
|
71 parent::__construct($translatable_string, $args, $options, $string_translation);
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * Constructs a new class instance from already translated markup.
|
Chris@0
|
76 *
|
Chris@0
|
77 * This method ensures that the string is pluralized correctly. As opposed
|
Chris@0
|
78 * to the __construct() method, this method is designed to be invoked with
|
Chris@0
|
79 * a string already translated (such as with configuration translation).
|
Chris@0
|
80 *
|
Chris@0
|
81 * @param int $count
|
Chris@0
|
82 * The item count to display.
|
Chris@0
|
83 * @param string $translated_string
|
Chris@0
|
84 * The already translated string.
|
Chris@0
|
85 * @param array $args
|
Chris@0
|
86 * An associative array of replacements to make after translation. Instances
|
Chris@0
|
87 * of any key in this array are replaced with the corresponding value.
|
Chris@0
|
88 * Based on the first character of the key, the value is escaped and/or
|
Chris@17
|
89 * themed. See \Drupal\Component\Render\FormattableMarkup. Note that you
|
Chris@0
|
90 * do not need to include @count in this array; this replacement is done
|
Chris@0
|
91 * automatically for the plural cases.
|
Chris@0
|
92 * @param array $options
|
Chris@0
|
93 * An associative array of additional options. See t() for allowed keys.
|
Chris@0
|
94 *
|
Chris@0
|
95 * @return \Drupal\Core\StringTranslation\PluralTranslatableMarkup
|
Chris@0
|
96 * A PluralTranslatableMarkup object.
|
Chris@0
|
97 */
|
Chris@0
|
98 public static function createFromTranslatedString($count, $translated_string, array $args = [], array $options = []) {
|
Chris@0
|
99 $plural = new static($count, '', '', $args, $options);
|
Chris@0
|
100 $plural->translatedString = $translated_string;
|
Chris@0
|
101 return $plural;
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 /**
|
Chris@0
|
105 * Renders the object as a string.
|
Chris@0
|
106 *
|
Chris@0
|
107 * @return string
|
Chris@0
|
108 * The translated string.
|
Chris@0
|
109 */
|
Chris@0
|
110 public function render() {
|
Chris@0
|
111 if (!$this->translatedString) {
|
Chris@0
|
112 $this->translatedString = $this->getStringTranslation()->translateString($this);
|
Chris@0
|
113 }
|
Chris@0
|
114 if ($this->translatedString === '') {
|
Chris@0
|
115 return '';
|
Chris@0
|
116 }
|
Chris@0
|
117
|
Chris@0
|
118 $arguments = $this->getArguments();
|
Chris@0
|
119 $arguments['@count'] = $this->count;
|
Chris@18
|
120 $translated_array = explode(PoItem::DELIMITER, $this->translatedString);
|
Chris@0
|
121
|
Chris@0
|
122 if ($this->count == 1) {
|
Chris@0
|
123 return $this->placeholderFormat($translated_array[0], $arguments);
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 $index = $this->getPluralIndex();
|
Chris@0
|
127 if ($index == 0) {
|
Chris@0
|
128 // Singular form.
|
Chris@0
|
129 $return = $translated_array[0];
|
Chris@0
|
130 }
|
Chris@0
|
131 else {
|
Chris@0
|
132 if (isset($translated_array[$index])) {
|
Chris@0
|
133 // N-th plural form.
|
Chris@0
|
134 $return = $translated_array[$index];
|
Chris@0
|
135 }
|
Chris@0
|
136 else {
|
Chris@0
|
137 // If the index cannot be computed or there's no translation, use the
|
Chris@0
|
138 // second plural form as a fallback (which allows for most flexibility
|
Chris@0
|
139 // with the replaceable @count value).
|
Chris@0
|
140 $return = $translated_array[1];
|
Chris@0
|
141 }
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 return $this->placeholderFormat($return, $arguments);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 /**
|
Chris@0
|
148 * Gets the plural index through the gettext formula.
|
Chris@0
|
149 *
|
Chris@0
|
150 * @return int
|
Chris@0
|
151 */
|
Chris@0
|
152 protected function getPluralIndex() {
|
Chris@0
|
153 // We have to test both if the function and the service exist since in
|
Chris@0
|
154 // certain situations it is possible that locale code might be loaded but
|
Chris@0
|
155 // the service does not exist. For example, where the parent test site has
|
Chris@0
|
156 // locale installed but the child site does not.
|
Chris@0
|
157 // @todo Refactor in https://www.drupal.org/node/2660338 so this code does
|
Chris@0
|
158 // not depend on knowing that the Locale module exists.
|
Chris@0
|
159 if (function_exists('locale_get_plural') && \Drupal::hasService('locale.plural.formula')) {
|
Chris@0
|
160 return locale_get_plural($this->count, $this->getOption('langcode'));
|
Chris@0
|
161 }
|
Chris@0
|
162 return -1;
|
Chris@0
|
163 }
|
Chris@0
|
164
|
Chris@0
|
165 /**
|
Chris@0
|
166 * {@inheritdoc}
|
Chris@0
|
167 */
|
Chris@0
|
168 public function __sleep() {
|
Chris@0
|
169 return array_merge(parent::__sleep(), ['count']);
|
Chris@0
|
170 }
|
Chris@0
|
171
|
Chris@0
|
172 }
|