Passed
Push — master ( bbc2d6...1cf49e )
by Sergei
02:53
created

Translator::getSimpleMessageFormatter()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Translator;
6
7
use Psr\EventDispatcher\EventDispatcherInterface;
8
use RuntimeException;
9
use Yiisoft\I18n\Locale;
10
use Yiisoft\Translator\Event\MissingTranslationCategoryEvent;
11
use Yiisoft\Translator\Event\MissingTranslationEvent;
12
13
/**
14
 * Translator translates a message into the specified language.
15
 */
16
final class Translator implements TranslatorInterface
17
{
18
    private MessageFormatterInterface $defaultMessageFormatter;
19
20
    private string $defaultCategory = 'app';
21
22
    /**
23
     * @var CategorySource[][] Array of category message sources indexed by category names.
24
     */
25
    private array $categorySources = [];
26
27
    /**
28
     * @psalm-var array<string,true>
29
     */
30
    private array $dispatchedMissingTranslationCategoryEvents = [];
31
32
    /**
33
     * @param string $locale Default locale to use if locale is not specified explicitly.
34
     * @param string|null $fallbackLocale Locale to use if message for the locale specified was not found. Null for none.
35
     * @param EventDispatcherInterface|null $eventDispatcher Event dispatcher for translation events. Null for none.
36
     */
37 56
    public function __construct(
38
        private string $locale = 'en_US',
39
        private ?string $fallbackLocale = null,
40
        private ?EventDispatcherInterface $eventDispatcher = null,
41
        ?MessageFormatterInterface $defaultMessageFormatter = null,
42
    ) {
43 56
        $this->defaultMessageFormatter = $defaultMessageFormatter ?? new NullMessageFormatter();
44
    }
45
46 52
    public function addCategorySource(CategorySource $category): void
47
    {
48 52
        if (isset($this->categorySources[$category->getName()])) {
49 14
            $this->categorySources[$category->getName()][] = $category;
50
        } else {
51 52
            $this->categorySources[$category->getName()] = [$category];
52
        }
53
    }
54
55 15
    public function addCategorySources(array $categories): void
56
    {
57 15
        foreach ($categories as $category) {
58 15
            $this->addCategorySource($category);
59
        }
60
    }
61
62 3
    public function setLocale(string $locale): void
63
    {
64 3
        $this->locale = $locale;
65
    }
66
67 4
    public function getLocale(): string
68
    {
69 4
        return $this->locale;
70
    }
71
72 53
    public function translate(
73
        string $id,
74
        array $parameters = [],
75
        string $category = null,
76
        string $locale = null
77
    ): string {
78 53
        $locale ??= $this->locale;
79
80 53
        $category ??= $this->defaultCategory;
81
82 53
        if (empty($this->categorySources[$category])) {
83 4
            $this->dispatchMissingTranslationCategoryEvent($category);
84 4
            return $this->defaultMessageFormatter->format($id, $parameters, $locale);
85
        }
86
87 49
        return $this->translateUsingCategorySources($id, $parameters, $category, $locale);
88
    }
89
90
    /**
91
     * @psalm-immutable
92
     */
93 2
    public function withCategory(string $category): self
94
    {
95 2
        if (!isset($this->categorySources[$category])) {
96 1
            throw new RuntimeException('Category with name "' . $category . '" does not exist.');
97
        }
98
99 1
        $new = clone $this;
100 1
        $new->defaultCategory = $category;
101 1
        return $new;
102
    }
103
104 1
    public function withLocale(string $locale): self
105
    {
106 1
        $new = clone $this;
107 1
        $new->setLocale($locale);
108 1
        return $new;
109
    }
110
111 49
    private function translateUsingCategorySources(
112
        string $id,
113
        array $parameters,
114
        string $category,
115
        string $locale
116
    ): string {
117 49
        $sourceCategory = end($this->categorySources[$category]);
118
        do {
119 49
            $message = $sourceCategory->getMessage($id, $locale, $parameters);
120
121 49
            if ($message !== null) {
122 36
                return $sourceCategory->format($message, $parameters, $locale);
123
            }
124
125 31
            if ($this->eventDispatcher !== null) {
126 12
                $this->eventDispatcher->dispatch(new MissingTranslationEvent($sourceCategory->getName(), $locale, $id));
127
            }
128 31
        } while (($sourceCategory = prev($this->categorySources[$category])) !== false);
129
130 26
        $localeObject = new Locale($locale);
131 26
        $fallback = $localeObject->fallbackLocale();
132
133 26
        if ($fallback->asString() !== $localeObject->asString()) {
134 14
            return $this->translateUsingCategorySources($id, $parameters, $category, $fallback->asString());
135
        }
136
137 19
        if (!empty($this->fallbackLocale)) {
138 10
            $fallbackLocaleObject = (new Locale($this->fallbackLocale))->fallbackLocale();
139 10
            if ($fallbackLocaleObject->asString() !== $localeObject->asString()) {
140 9
                return $this->translateUsingCategorySources($id, $parameters, $category, $fallbackLocaleObject->asString());
141
            }
142
        }
143
144 13
        $categorySource = end($this->categorySources[$category]);
145 13
        return $categorySource->format($id, $parameters, $locale);
146
    }
147
148 4
    private function dispatchMissingTranslationCategoryEvent(string $category): void
149
    {
150
        if (
151 4
            $this->eventDispatcher !== null
152 4
            && !isset($this->dispatchedMissingTranslationCategoryEvents[$category])
153
        ) {
154 2
            $this->dispatchedMissingTranslationCategoryEvents[$category] = true;
155 2
            $this->eventDispatcher->dispatch(new MissingTranslationCategoryEvent($category));
156
        }
157
    }
158
}
159