Completed
Push — master ( b18242...9fb221 )
by Mārtiņš
01:55
created

MessageStorage::translationToItem()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3.0013

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 18
cts 19
cp 0.9474
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 18
nc 3
nop 3
crap 3.0013
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 25
    public function __construct(MessageRepositoryInterface $repository)
27
    {
28 25
        $this->repository = $repository;
29 25
    }
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 repository by merging it to a previously saved version.
57
     *
58
     * @param string $locale
59
     * @param string $domain
60
     * @param Translation $translation
61
     */
62 20
    public function saveSingleTranslation(string $locale, string $domain, Translation $translation)
63
    {
64
        // Make a clone so we don't modify the passed instance
65 20
        $translation = $translation->getClone();
66
67 20
        $key = $this->getKey($locale, $domain, $translation);
68
69 20
        $existingItem = $this->repository->getSingle($key);
70
71 20
        if ($existingItem->exists()) {
72 5
            $existingTranslation = $this->itemToTranslation($existingItem);
73 5
            $translation->mergeWith($existingTranslation);
74 5
            $item = $this->translationToItem($locale, $domain, $translation);
75
76
            // Override the disabled state of the existing translation
77 5
            $item->isDisabled = $translation->isDisabled();
78
        } else {
79 20
            $item = $this->translationToItem($locale, $domain, $translation);
80
        }
81
82 20
        $item->hasOriginalTranslation = $translation->hasTranslation();
83 20
        $item->requiresTranslating = $this->requiresTranslating($locale, $translation);
84
85 20
        $this->repository->save($item);
86 20
    }
87
88
    /**
89
     * Check if some translations are missing (original or missing plural forms)
90
     *
91
     * @param string $locale
92
     * @param Translation $translation
93
     * @return bool
94
     */
95 20
    private function requiresTranslating(string $locale, Translation $translation): bool
96
    {
97 20
        if (!$translation->hasTranslation()) {
98 6
            return true;
99
        }
100
101 17
        if ($translation->hasPlural()) {
102 5
            $translatedPluralCount = count(array_filter($translation->getPluralTranslations()));
103
            // If there are less plural translations than language requires, this needs translating
104 5
            return $this->getPluralCount($locale) !== $translatedPluralCount;
105
        }
106
107 12
        return false;
108
    }
109
110
    /**
111
     * Get number of plural forms for this locale
112
     *
113
     * @param string $locale
114
     * @return int
115
     * @throws \InvalidArgumentException Thrown in the locale is not correct
116
     */
117 5
    private function getPluralCount(string $locale): int
118
    {
119 5
        if (!array_key_exists($locale, $this->pluralFormCache)) {
120 5
            $info = Language::getById($locale);
121 5
            $this->pluralFormCache[$locale] = count($info->categories);
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 20
    private function getKey(string $locale, string $domain, Translation $translation): string
136
    {
137 20
        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 16
    private function itemToTranslation(MessageItem $item): Translation
147
    {
148 16
        $translation = new Translation($item->context, $item->original, $item->originalPlural);
149
150 16
        $translation->setTranslation($item->translation);
151 16
        $translation->setPluralTranslations($item->pluralTranslations);
152 16
        $translation->setDisabled($item->isDisabled);
153
154 16
        foreach ($item->references as $v) {
155 1
            $translation->addReference($v[0], $v[1]);
156
        }
157
158 16
        foreach ($item->comments as $v) {
159 1
            $translation->addComment($v);
160
        }
161
162 16
        foreach ($item->extractedComments as $v) {
163 1
            $translation->addExtractedComment($v);
164
        }
165
166 16
        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 20
    private function translationToItem(string $locale, string $domain, Translation $translation): MessageItem
178
    {
179 20
        $item = new MessageItem;
180
181 20
        $item->key = $this->getKey($locale, $domain, $translation);
182 20
        $item->domain = $domain;
183 20
        $item->locale = $locale;
184 20
        $item->context = (string)$translation->getContext();
185 20
        $item->original = $translation->getOriginal();
186 20
        $item->translation = $translation->getTranslation();
187 20
        $item->originalPlural = $translation->getPlural();
188 20
        $item->pluralTranslations = $translation->getPluralTranslations();
189 20
        $item->references = $translation->getReferences();
190 20
        $item->comments = $translation->getComments();
191 20
        $item->extractedComments = $translation->getExtractedComments();
192 20
        $item->isDisabled = $translation->isDisabled();
193
194 20
        foreach ($item->references as $reference) {
195 1
            if ($this->isJsFile($reference[0] ?? '')) {
196
                $item->isJs = true;
197 1
                break;
198
            }
199
        }
200
201 20
        return $item;
202
    }
203
204
    /**
205
     * Check if this is considered a JS file.
206
     *
207
     * @param string $filename
208
     * @return bool
209
     */
210 1
    private function isJsFile(string $filename): bool
211
    {
212 1
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
213
214 1
        return in_array($extension, ['vue', 'js'], true);
215
    }
216
217
    /**
218
     * All translations, including disabled, enabled and untranslated
219
     *
220
     * @param string $locale
221
     * @param $domain
222
     * @return Translations
223
     */
224 4
    public function getAll(string $locale, $domain): Translations
225
    {
226 4
        return $this->convertItems(
227 4
            $locale,
228 4
            (string)$domain,
229 4
            $this->repository->getAll($locale, (string)$domain)
230
        );
231
    }
232
233
    /**
234
     * All enabled translations including untranslated
235
     *
236
     * @param string $locale
237
     * @param $domain
238
     * @return Translations
239
     */
240 2
    public function getAllEnabled(string $locale, $domain): Translations
241
    {
242 2
        return $this->convertItems(
243 2
            $locale,
244 2
            (string)$domain,
245 2
            $this->repository->getEnabled($locale, (string)$domain)
246
        );
247
    }
248
249
    /**
250
     * Enabled and translated translations only
251
     *
252
     * @param string $locale
253
     * @param $domain
254
     * @return Translations
255
     */
256 10
    public function getEnabledTranslated(string $locale, $domain): Translations
257
    {
258 10
        $items = $this->repository->getEnabledTranslated($locale, (string)$domain);
259
260 10
        return $this->convertItems($locale, (string)$domain, $items);
261
    }
262
263
    /**
264
     * Enabled and translated JS translations
265
     *
266
     * @param string $locale
267
     * @param $domain
268
     * @return Translations
269
     */
270
    public function getEnabledTranslatedJs(string $locale, $domain): Translations
271
    {
272
        $items = $this->repository->getEnabledTranslatedJs($locale, (string)$domain);
273
274
        return $this->convertItems($locale, (string)$domain, $items);
275
    }
276
277
    /**
278
     * Enabled and translated translations only
279
     *
280
     * @param string $locale
281
     * @param $domain
282
     * @return Translations
283
     */
284 6
    public function getRequiresTranslating(string $locale, $domain): Translations
285
    {
286 6
        return $this->convertItems(
287 6
            $locale,
288 6
            (string)$domain,
289 6
            $this->repository->getRequiresTranslating($locale, (string)$domain)
290
        );
291
    }
292
293
    /**
294
     * Converts message items to a translation object
295
     *
296
     * @param string $locale
297
     * @param string|null $domain
298
     * @param MessageItem[] $items
299
     * @return Translations
300
     */
301 20
    private function convertItems(string $locale, $domain, array $items): Translations
302
    {
303 20
        $domain = (string)$domain;
304
305 20
        $translations = new Translations;
306 20
        $translations->setDomain($domain);
307 20
        $translations->setLanguage($locale);
308
309 20
        foreach ($items as $v) {
310 16
            $translation = $this->itemToTranslation($v);
311 16
            $translations[] = $translation;
312
        }
313
314 20
        return $translations;
315
    }
316
317
    /**
318
     * Mark all messages in the given domain and locale as disabled
319
     *
320
     * @param string $locale
321
     * @param string $domain
322
     */
323 2
    public function disableAll(string $locale, string $domain)
324
    {
325 2
        $this->repository->disableAll($locale, $domain);
326
    }
327
}