Passed
Pull Request — master (#74)
by Sergei
12:07
created

Translator::withLocale()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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