blob: 7aa27efdad216d8ca0851d0d31a6ba2e615f3be4 [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{
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010022 private array $messages = [];
23 private array $metadata = [];
24 private array $resources = [];
25 private string $locale;
26 private $fallbackCatalogue = null;
27 private ?self $parent = null;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020028
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010041 public function getLocale(): string
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020042 {
43 return $this->locale;
44 }
45
46 /**
47 * {@inheritdoc}
48 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010049 public function getDomains(): array
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020050 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010066 public function all(string $domain = null): array
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020067 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100102 public function has(string $id, string $domain = 'messages'): bool
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200103 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100118 public function defines(string $id, string $domain = 'messages'): bool
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200119 {
120 return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]);
121 }
122
123 /**
124 * {@inheritdoc}
125 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100126 public function get(string $id, string $domain = 'messages'): string
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200127 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100236 public function getFallbackCatalogue(): ?MessageCatalogueInterface
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200237 {
238 return $this->fallbackCatalogue;
239 }
240
241 /**
242 * {@inheritdoc}
243 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100244 public function getResources(): array
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200245 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100260 public function getMetadata(string $key = '', string $domain = 'messages'): mixed
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200261 {
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100282 public function setMetadata(string $key, mixed $value, string $domain = 'messages')
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200283 {
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}