blob: ff49b5a97e15e9d2038d8bfe3017d7126f45a19b [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Translation;
13
14use Symfony\Component\Config\Resource\ResourceInterface;
15use Symfony\Component\Translation\Exception\LogicException;
16
17/**
18 * @author Fabien Potencier <fabien@symfony.com>
19 */
20class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface
21{
22 private $messages = [];
23 private $metadata = [];
24 private $resources = [];
25 private $locale;
26 private $fallbackCatalogue;
27 private $parent;
28
29 /**
30 * @param array $messages An array of messages classified by domain
31 */
32 public function __construct(string $locale, array $messages = [])
33 {
34 $this->locale = $locale;
35 $this->messages = $messages;
36 }
37
38 /**
39 * {@inheritdoc}
40 */
41 public function getLocale()
42 {
43 return $this->locale;
44 }
45
46 /**
47 * {@inheritdoc}
48 */
49 public function getDomains()
50 {
51 $domains = [];
52
53 foreach ($this->messages as $domain => $messages) {
54 if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
55 $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX));
56 }
57 $domains[$domain] = $domain;
58 }
59
60 return array_values($domains);
61 }
62
63 /**
64 * {@inheritdoc}
65 */
66 public function all(string $domain = null)
67 {
68 if (null !== $domain) {
69 // skip messages merge if intl-icu requested explicitly
70 if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
71 return $this->messages[$domain] ?? [];
72 }
73
74 return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []);
75 }
76
77 $allMessages = [];
78
79 foreach ($this->messages as $domain => $messages) {
80 if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
81 $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX));
82 $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []);
83 } else {
84 $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages;
85 }
86 }
87
88 return $allMessages;
89 }
90
91 /**
92 * {@inheritdoc}
93 */
94 public function set(string $id, string $translation, string $domain = 'messages')
95 {
96 $this->add([$id => $translation], $domain);
97 }
98
99 /**
100 * {@inheritdoc}
101 */
102 public function has(string $id, string $domain = 'messages')
103 {
104 if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
105 return true;
106 }
107
108 if (null !== $this->fallbackCatalogue) {
109 return $this->fallbackCatalogue->has($id, $domain);
110 }
111
112 return false;
113 }
114
115 /**
116 * {@inheritdoc}
117 */
118 public function defines(string $id, string $domain = 'messages')
119 {
120 return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]);
121 }
122
123 /**
124 * {@inheritdoc}
125 */
126 public function get(string $id, string $domain = 'messages')
127 {
128 if (isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) {
129 return $this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id];
130 }
131
132 if (isset($this->messages[$domain][$id])) {
133 return $this->messages[$domain][$id];
134 }
135
136 if (null !== $this->fallbackCatalogue) {
137 return $this->fallbackCatalogue->get($id, $domain);
138 }
139
140 return $id;
141 }
142
143 /**
144 * {@inheritdoc}
145 */
146 public function replace(array $messages, string $domain = 'messages')
147 {
148 unset($this->messages[$domain], $this->messages[$domain.self::INTL_DOMAIN_SUFFIX]);
149
150 $this->add($messages, $domain);
151 }
152
153 /**
154 * {@inheritdoc}
155 */
156 public function add(array $messages, string $domain = 'messages')
157 {
158 if (!isset($this->messages[$domain])) {
159 $this->messages[$domain] = [];
160 }
161 $intlDomain = $domain;
162 if (!str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) {
163 $intlDomain .= self::INTL_DOMAIN_SUFFIX;
164 }
165 foreach ($messages as $id => $message) {
166 if (isset($this->messages[$intlDomain]) && \array_key_exists($id, $this->messages[$intlDomain])) {
167 $this->messages[$intlDomain][$id] = $message;
168 } else {
169 $this->messages[$domain][$id] = $message;
170 }
171 }
172 }
173
174 /**
175 * {@inheritdoc}
176 */
177 public function addCatalogue(MessageCatalogueInterface $catalogue)
178 {
179 if ($catalogue->getLocale() !== $this->locale) {
180 throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".', $catalogue->getLocale(), $this->locale));
181 }
182
183 foreach ($catalogue->all() as $domain => $messages) {
184 if ($intlMessages = $catalogue->all($domain.self::INTL_DOMAIN_SUFFIX)) {
185 $this->add($intlMessages, $domain.self::INTL_DOMAIN_SUFFIX);
186 $messages = array_diff_key($messages, $intlMessages);
187 }
188 $this->add($messages, $domain);
189 }
190
191 foreach ($catalogue->getResources() as $resource) {
192 $this->addResource($resource);
193 }
194
195 if ($catalogue instanceof MetadataAwareInterface) {
196 $metadata = $catalogue->getMetadata('', '');
197 $this->addMetadata($metadata);
198 }
199 }
200
201 /**
202 * {@inheritdoc}
203 */
204 public function addFallbackCatalogue(MessageCatalogueInterface $catalogue)
205 {
206 // detect circular references
207 $c = $catalogue;
208 while ($c = $c->getFallbackCatalogue()) {
209 if ($c->getLocale() === $this->getLocale()) {
210 throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
211 }
212 }
213
214 $c = $this;
215 do {
216 if ($c->getLocale() === $catalogue->getLocale()) {
217 throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale()));
218 }
219
220 foreach ($catalogue->getResources() as $resource) {
221 $c->addResource($resource);
222 }
223 } while ($c = $c->parent);
224
225 $catalogue->parent = $this;
226 $this->fallbackCatalogue = $catalogue;
227
228 foreach ($catalogue->getResources() as $resource) {
229 $this->addResource($resource);
230 }
231 }
232
233 /**
234 * {@inheritdoc}
235 */
236 public function getFallbackCatalogue()
237 {
238 return $this->fallbackCatalogue;
239 }
240
241 /**
242 * {@inheritdoc}
243 */
244 public function getResources()
245 {
246 return array_values($this->resources);
247 }
248
249 /**
250 * {@inheritdoc}
251 */
252 public function addResource(ResourceInterface $resource)
253 {
254 $this->resources[$resource->__toString()] = $resource;
255 }
256
257 /**
258 * {@inheritdoc}
259 */
260 public function getMetadata(string $key = '', string $domain = 'messages')
261 {
262 if ('' == $domain) {
263 return $this->metadata;
264 }
265
266 if (isset($this->metadata[$domain])) {
267 if ('' == $key) {
268 return $this->metadata[$domain];
269 }
270
271 if (isset($this->metadata[$domain][$key])) {
272 return $this->metadata[$domain][$key];
273 }
274 }
275
276 return null;
277 }
278
279 /**
280 * {@inheritdoc}
281 */
282 public function setMetadata(string $key, $value, string $domain = 'messages')
283 {
284 $this->metadata[$domain][$key] = $value;
285 }
286
287 /**
288 * {@inheritdoc}
289 */
290 public function deleteMetadata(string $key = '', string $domain = 'messages')
291 {
292 if ('' == $domain) {
293 $this->metadata = [];
294 } elseif ('' == $key) {
295 unset($this->metadata[$domain]);
296 } else {
297 unset($this->metadata[$domain][$key]);
298 }
299 }
300
301 /**
302 * Adds current values with the new values.
303 *
304 * @param array $values Values to add
305 */
306 private function addMetadata(array $values)
307 {
308 foreach ($values as $domain => $keys) {
309 foreach ($keys as $key => $value) {
310 $this->setMetadata($key, $value, $domain);
311 }
312 }
313 }
314}