Completed
Push — master ( 80539d...151257 )
by Mārtiņš
02:11
created

MessageStorage::getPluralCount()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
crap 3.0261
1
<?php
2
3
namespace Printful\GettextCms;
4
5
use Gettext\Languages\Language;
6
use Gettext\Translation;
7
use Gettext\Translations;
8
use Printful\GettextCms\Exceptions\InvalidTranslationException;
9
use Printful\GettextCms\Interfaces\MessageRepositoryInterface;
10
use Printful\GettextCms\Structures\MessageItem;
11
12
/**
13
 * Class handles all saving and retrieving logic of translations using the given repository
14
 */
15
class MessageStorage
16
{
17
    /** @var MessageRepositoryInterface */
18
    private $repository;
19
20
    /**
21
     * Cache for plural form counts
22
     * @var array [locale => plural count, ..]
23
     */
24
    private $pluralFormCache = [];
25
26 17
    public function __construct(MessageRepositoryInterface $repository)
27
    {
28 17
        $this->repository = $repository;
29 17
    }
30
31
    /**
32
     * Save translation object for a single domain and locale
33
     *
34
     * @param Translations $translations
35
     * @throws InvalidTranslationException
36
     */
37 6
    public function saveTranslations(Translations $translations)
38
    {
39 6
        $locale = $translations->getLanguage();
40 6
        $domain = (string)$translations->getDomain();
41
42 6
        if (!$locale) {
43 1
            throw new InvalidTranslationException('Locale is missing');
44
        }
45
46 5
        if (!$domain) {
47 1
            throw new InvalidTranslationException('Domain is missing');
48
        }
49
50 4
        foreach ($translations as $v) {
51 4
            $this->saveSingleTranslation($locale, $domain, $v);
52
        }
53 4
    }
54
55
    /**
56
     * Save a translation to database by merging it to a previously saved version.
57
     *
58
     * @param string $locale
59
     * @param string $domain
60
     * @param Translation $translation
61
     */
62 12
    public function saveSingleTranslation(string $locale, string $domain, Translation $translation)
63
    {
64 12
        $key = $this->getKey($locale, $domain, $translation);
65
66 12
        $existingItem = $this->repository->getSingle($key);
67
68 12
        if ($existingItem->exists()) {
69 2
            $existingTranslation = $this->itemToTranslation($existingItem);
70 2
            $existingTranslation->mergeWith($translation);
71 2
            $item = $this->translationToItem($locale, $domain, $existingTranslation);
72
73
            // Override the disabled state of the existing translation
74 2
            $item->isDisabled = $translation->isDisabled();
75
        } else {
76 12
            $item = $this->translationToItem($locale, $domain, $translation);
77
        }
78
79 12
        $item->hasOriginalTranslation = $translation->hasTranslation();
80 12
        $item->requiresTranslating = $this->requiresTranslating($locale, $translation);
81
82 12
        $this->repository->save($item);
83 12
    }
84
85
    /**
86
     * Check if some translations are missing (original or missing plural forms)
87
     *
88
     * @param string $locale
89
     * @param Translation $translation
90
     * @return bool
91
     */
92 12
    private function requiresTranslating(string $locale, Translation $translation): bool
93
    {
94 12
        if (!$translation->hasTranslation()) {
95 7
            return true;
96
        }
97
98 9
        if ($translation->hasPlural()) {
99 5
            $translatedPluralCount = count(array_filter($translation->getPluralTranslations()));
100
            // If there are less plural translations than language requires, this needs translating
101 5
            return $this->getPluralCount($locale) !== $translatedPluralCount;
102
        }
103
104 4
        return false;
105
    }
106
107
    /**
108
     * Get number of plural forms for this locale
109
     *
110
     * @param string $locale
111
     * @return int
112
     */
113 5
    private function getPluralCount(string $locale): int
114
    {
115 5
        if (!array_key_exists($locale, $this->pluralFormCache)) {
116 5
            $info = Language::getById($locale);
117 5
            if ($info) {
118 5
                $this->pluralFormCache[$locale] = count($info->categories);
119
            } else {
120
                $this->pluralFormCache[$locale] = 2; // Graceful fallback to two forms - zero and multiple
121
            }
122
        }
123
124 5
        return $this->pluralFormCache[$locale];
125
    }
126
127
    /**
128
     * Generate a unique key for storage (basically a primary key)
129
     *
130
     * @param string $locale
131
     * @param string $domain
132
     * @param Translation $translation
133
     * @return string
134
     */
135 12
    private function getKey(string $locale, string $domain, Translation $translation): string
136
    {
137 12
        return md5($locale . '|' . $domain . '|' . $translation->getContext() . '|' . $translation->getOriginal());
138
    }
139
140
    /**
141
     * Convert a message item to a translation item
142
     *
143
     * @param MessageItem $item
144
     * @return Translation
145
     */
146 11
    private function itemToTranslation(MessageItem $item): Translation
147
    {
148 11
        $translation = new Translation($item->context, $item->original, $item->originalPlural);
149
150 11
        $translation->setTranslation($item->translation);
151 11
        $translation->setPluralTranslations($item->pluralTranslations);
152 11
        $translation->setDisabled($item->isDisabled);
153
154 11
        foreach ($item->references as $v) {
155 1
            $translation->addReference($v[0], $v[1]);
156
        }
157
158 11
        foreach ($item->comments as $v) {
159 1
            $translation->addComment($v);
160
        }
161
162 11
        foreach ($item->extractedComments as $v) {
163 1
            $translation->addExtractedComment($v);
164
        }
165
166 11
        return $translation;
167
    }
168
169
    /**
170
     * Convert a translation item to a message item
171
     *
172
     * @param string $locale
173
     * @param string $domain
174
     * @param Translation $translation
175
     * @return MessageItem
176
     */
177 12
    private function translationToItem(string $locale, string $domain, Translation $translation): MessageItem
178
    {
179 12
        $item = new MessageItem;
180
181 12
        $item->key = $this->getKey($locale, $domain, $translation);
182 12
        $item->domain = $domain;
183 12
        $item->locale = $locale;
184 12
        $item->context = (string)$translation->getContext();
185 12
        $item->original = $translation->getOriginal();
186 12
        $item->translation = $translation->getTranslation();
187 12
        $item->originalPlural = $translation->getPlural();
188 12
        $item->pluralTranslations = $translation->getPluralTranslations();
189 12
        $item->references = $translation->getReferences();
190 12
        $item->comments = $translation->getComments();
191 12
        $item->extractedComments = $translation->getExtractedComments();
192 12
        $item->isDisabled = $translation->isDisabled();
193
194 12
        return $item;
195
    }
196
197
    /**
198
     * All translations, including disabled, enabled and untranslated
199
     *
200
     * @param string $locale
201
     * @param $domain
202
     * @return Translations
203
     */
204 6
    public function getAll(string $locale, $domain): Translations
205
    {
206 6
        return $this->convertItems(
207 6
            $locale,
208 6
            (string)$domain,
209 6
            $this->repository->getAll($locale, (string)$domain)
210
        );
211
    }
212
213
    /**
214
     * All enabled translations including untranslated
215
     *
216
     * @param string $locale
217
     * @param $domain
218
     * @return Translations
219
     */
220 2
    public function getAllEnabled(string $locale, $domain): Translations
221
    {
222 2
        return $this->convertItems(
223 2
            $locale,
224 2
            (string)$domain,
225 2
            $this->repository->getEnabled($locale, (string)$domain)
226
        );
227
    }
228
229
    /**
230
     * Enabled and translated translations only
231
     *
232
     * @param string $locale
233
     * @param $domain
234
     * @return Translations
235
     */
236 2
    public function getEnabledTranslated(string $locale, $domain): Translations
237
    {
238 2
        return $this->convertItems(
239 2
            $locale,
240 2
            (string)$domain,
241 2
            $this->repository->getEnabledTranslated($locale, (string)$domain)
242
        );
243
    }
244
245
    /**
246
     * Enabled and translated translations only
247
     *
248
     * @param string $locale
249
     * @param $domain
250
     * @return Translations
251
     */
252 6
    public function getRequiresTranslating(string $locale, $domain): Translations
253
    {
254 6
        return $this->convertItems(
255 6
            $locale,
256 6
            (string)$domain,
257 6
            $this->repository->getRequiresTranslating($locale, (string)$domain)
258
        );
259
    }
260
261
    /**
262
     * Converts message items to a translation object
263
     *
264
     * @param string $locale
265
     * @param string|null $domain
266
     * @param MessageItem[] $items
267
     * @return Translations
268
     */
269 14
    private function convertItems(string $locale, $domain, array $items): Translations
270
    {
271 14
        $domain = (string)$domain;
272
273 14
        $translations = new Translations;
274 14
        $translations->setDomain($domain);
275 14
        $translations->setLanguage($locale);
276
277 14
        foreach ($items as $v) {
278 11
            $translation = $this->itemToTranslation($v);
279 11
            $translations[] = $translation;
280
        }
281
282 14
        return $translations;
283
    }
284
285
    /**
286
     * Mark all messages in the given domain and locale as disabled
287
     *
288
     * @param string $locale
289
     * @param string $domain
290
     */
291 2
    public function disableAll(string $locale, string $domain)
292
    {
293 2
        $this->repository->disableAll($locale, $domain);
294
    }
295
}