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