Passed
Push — master ( 6944c8...80e2d3 )
by Alexander
03:04 queued 59s
created

Translator::translateUsingCategorySources()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 35
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7

Importance

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