Passed
Pull Request — master (#74)
by Sergei
02:18
created

dispatchMissingTranslationCategoryEvent()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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