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