Completed
Push — master ( c51ac3...81e6e0 )
by Oscar
01:25 queued 11s
created

Translations   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 399
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 399
rs 8.8
c 0
b 0
f 0
wmc 45
lcom 2
cbo 3

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 2
A __callStatic() 0 8 2
A __call() 0 22 4
A __clone() 0 10 2
A offsetSet() 0 20 3
A setPluralForms() 0 9 2
A getPluralForms() 0 10 3
A setHeader() 0 7 1
A getHeader() 0 4 2
A getHeaders() 0 8 2
A deleteHeaders() 0 6 1
A deleteHeader() 0 6 1
A getLanguage() 0 4 1
A setLanguage() 0 10 2
A hasLanguage() 0 6 3
A setDomain() 0 6 1
A getDomain() 0 4 1
A hasDomain() 0 6 3
A find() 0 10 3
A countTranslated() 0 10 3
A insert() 0 4 1
A mergeWith() 0 7 1
A createNewTranslation() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like Translations often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Translations, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Gettext;
4
5
use Gettext\Languages\Language;
6
use BadMethodCallException;
7
use InvalidArgumentException;
8
use ArrayObject;
9
10
/**
11
 * Class to manage a collection of translations.
12
 *
13
 * @method static $this fromBladeFile(string $filename, array $options = [])
14
 * @method static $this fromBladeString(string $string, array $options = [])
15
 * @method $this addFromBladeFile(string $filename, array $options = [])
16
 * @method $this addFromBladeString(string $string, array $options = [])
17
 * @method static $this fromCsvFile(string $filename, array $options = [])
18
 * @method static $this fromCsvString(string $string, array $options = [])
19
 * @method $this addFromCsvFile(string $filename, array $options = [])
20
 * @method $this addFromCsvString(string $string, array $options = [])
21
 * @method bool toCsvFile(string $filename, array $options = [])
22
 * @method string toCsvString(array $options = [])
23
 * @method static $this fromCsvDictionaryFile(string $filename, array $options = [])
24
 * @method static $this fromCsvDictionaryString(string $string, array $options = [])
25
 * @method $this addFromCsvDictionaryFile(string $filename, array $options = [])
26
 * @method $this addFromCsvDictionaryString(string $string, array $options = [])
27
 * @method bool toCsvDictionaryFile(string $filename, array $options = [])
28
 * @method string toCsvDictionaryString(array $options = [])
29
 * @method static $this fromJedFile(string $filename, array $options = [])
30
 * @method static $this fromJedString(string $string, array $options = [])
31
 * @method $this addFromJedFile(string $filename, array $options = [])
32
 * @method $this addFromJedString(string $string, array $options = [])
33
 * @method bool toJedFile(string $filename, array $options = [])
34
 * @method string toJedString(array $options = [])
35
 * @method static $this fromJsCodeFile(string $filename, array $options = [])
36
 * @method static $this fromJsCodeString(string $string, array $options = [])
37
 * @method $this addFromJsCodeFile(string $filename, array $options = [])
38
 * @method $this addFromJsCodeString(string $string, array $options = [])
39
 * @method static $this fromJsonFile(string $filename, array $options = [])
40
 * @method static $this fromJsonString(string $string, array $options = [])
41
 * @method $this addFromJsonFile(string $filename, array $options = [])
42
 * @method $this addFromJsonString(string $string, array $options = [])
43
 * @method bool toJsonFile(string $filename, array $options = [])
44
 * @method string toJsonString(array $options = [])
45
 * @method static $this fromJsonDictionaryFile(string $filename, array $options = [])
46
 * @method static $this fromJsonDictionaryString(string $string, array $options = [])
47
 * @method $this addFromJsonDictionaryFile(string $filename, array $options = [])
48
 * @method $this addFromJsonDictionaryString(string $string, array $options = [])
49
 * @method bool toJsonDictionaryFile(string $filename, array $options = [])
50
 * @method string toJsonDictionaryString(array $options = [])
51
 * @method static $this fromMoFile(string $filename, array $options = [])
52
 * @method static $this fromMoString(string $string, array $options = [])
53
 * @method $this addFromMoFile(string $filename, array $options = [])
54
 * @method $this addFromMoString(string $string, array $options = [])
55
 * @method bool toMoFile(string $filename, array $options = [])
56
 * @method string toMoString(array $options = [])
57
 * @method static $this fromPhpArrayFile(string $filename, array $options = [])
58
 * @method static $this fromPhpArrayString(string $string, array $options = [])
59
 * @method $this addFromPhpArrayFile(string $filename, array $options = [])
60
 * @method $this addFromPhpArrayString(string $string, array $options = [])
61
 * @method bool toPhpArrayFile(string $filename, array $options = [])
62
 * @method string toPhpArrayString(array $options = [])
63
 * @method static $this fromPhpCodeFile(string $filename, array $options = [])
64
 * @method static $this fromPhpCodeString(string $string, array $options = [])
65
 * @method $this addFromPhpCodeFile(string $filename, array $options = [])
66
 * @method $this addFromPhpCodeString(string $string, array $options = [])
67
 * @method static $this fromPoFile(string $filename, array $options = [])
68
 * @method static $this fromPoString(string $string, array $options = [])
69
 * @method $this addFromPoFile(string $filename, array $options = [])
70
 * @method $this addFromPoString(string $string, array $options = [])
71
 * @method bool toPoFile(string $filename, array $options = [])
72
 * @method string toPoString(array $options = [])
73
 * @method static $this fromTwigFile(string $filename, array $options = [])
74
 * @method static $this fromTwigString(string $string, array $options = [])
75
 * @method $this addFromTwigFile(string $filename, array $options = [])
76
 * @method $this addFromTwigString(string $string, array $options = [])
77
 * @method static $this fromVueJsFile(string $filename, array $options = [])
78
 * @method static $this fromVueJsString(string $filename, array $options = [])
79
 * @method $this addFromVueJsFile(string $filename, array $options = [])
80
 * @method $this addFromVueJsString(string $filename, array $options = [])
81
 * @method static $this fromXliffFile(string $filename, array $options = [])
82
 * @method static $this fromXliffString(string $string, array $options = [])
83
 * @method $this addFromXliffFile(string $filename, array $options = [])
84
 * @method $this addFromXliffString(string $string, array $options = [])
85
 * @method bool toXliffFile(string $filename, array $options = [])
86
 * @method string toXliffString(array $options = [])
87
 * @method static $this fromYamlFile(string $filename, array $options = [])
88
 * @method static $this fromYamlString(string $string, array $options = [])
89
 * @method $this addFromYamlFile(string $filename, array $options = [])
90
 * @method $this addFromYamlString(string $string, array $options = [])
91
 * @method bool toYamlFile(string $filename, array $options = [])
92
 * @method string toYamlString(array $options = [])
93
 * @method static $this fromYamlDictionaryFile(string $filename, array $options = [])
94
 * @method static $this fromYamlDictionaryString(string $string, array $options = [])
95
 * @method $this addFromYamlDictionaryFile(string $filename, array $options = [])
96
 * @method $this addFromYamlDictionaryString(string $string, array $options = [])
97
 * @method bool toYamlDictionaryFile(string $filename, array $options = [])
98
 * @method string toYamlDictionaryString(array $options = [])
99
 */
100
class Translations extends ArrayObject
101
{
102
    const HEADER_LANGUAGE = 'Language';
103
    const HEADER_PLURAL = 'Plural-Forms';
104
    const HEADER_DOMAIN = 'X-Domain';
105
106
    public static $options = [
107
        'defaultHeaders' => [
108
            'Project-Id-Version' => '',
109
            'Report-Msgid-Bugs-To' => '',
110
            'Last-Translator' => '',
111
            'Language-Team' => '',
112
            'MIME-Version' => '1.0',
113
            'Content-Type' => 'text/plain; charset=UTF-8',
114
            'Content-Transfer-Encoding' => '8bit',
115
        ],
116
        'headersSorting' => false,
117
        'defaultDateHeaders' => [
118
            'POT-Creation-Date',
119
            'PO-Revision-Date',
120
        ],
121
    ];
122
123
    protected $headers;
124
125
    protected $translationClass;
126
127
    /**
128
     * @see ArrayObject::__construct()
129
     */
130
    public function __construct(
131
        $input = [],
132
        $flags = 0,
133
        $iterator_class = 'ArrayIterator',
134
        $translationClass = 'Gettext\Translation'
135
    ) {
136
        $this->headers = static::$options['defaultHeaders'];
137
138
        foreach (static::$options['defaultDateHeaders'] as $header) {
139
            $this->headers[$header] = date('c');
140
        }
141
142
        $this->headers[self::HEADER_LANGUAGE] = '';
143
144
        $this->translationClass = $translationClass;
145
146
        parent::__construct($input, $flags, $iterator_class);
147
    }
148
149
    /**
150
     * Magic method to create new instances using extractors
151
     * For example: Translations::fromMoFile($filename, $options);.
152
     *
153
     * @return Translations
154
     */
155
    public static function __callStatic($name, $arguments)
156
    {
157
        if (!preg_match('/^from(\w+)(File|String)$/i', $name, $matches)) {
158
            throw new BadMethodCallException("The method $name does not exists");
159
        }
160
161
        return call_user_func_array([new static(), 'add'.ucfirst($name)], $arguments);
162
    }
163
164
    /**
165
     * Magic method to import/export the translations to a specific format
166
     * For example: $translations->toMoFile($filename, $options);
167
     * For example: $translations->addFromMoFile($filename, $options);.
168
     *
169
     * @return self|bool
170
     */
171
    public function __call($name, $arguments)
172
    {
173
        if (!preg_match('/^(addFrom|to)(\w+)(File|String)$/i', $name, $matches)) {
174
            throw new BadMethodCallException("The method $name does not exists");
175
        }
176
177
        if ($matches[1] === 'addFrom') {
178
            $extractor = 'Gettext\\Extractors\\'.$matches[2].'::from'.$matches[3];
179
            $source = array_shift($arguments);
180
            $options = array_shift($arguments) ?: [];
181
182
            call_user_func($extractor, $source, $this, $options);
183
184
            return $this;
185
        }
186
187
        $generator = 'Gettext\\Generators\\'.$matches[2].'::to'.$matches[3];
188
189
        array_unshift($arguments, $this);
190
191
        return call_user_func_array($generator, $arguments);
192
    }
193
194
    /**
195
     * Magic method to clone each translation on clone the translations object.
196
     */
197
    public function __clone()
198
    {
199
        $array = [];
200
201
        foreach ($this as $key => $translation) {
202
            $array[$key] = clone $translation;
203
        }
204
205
        $this->exchangeArray($array);
206
    }
207
208
    /**
209
     * Control the new translations added.
210
     *
211
     * @param mixed       $index
212
     * @param Translation $value
213
     *
214
     * @throws InvalidArgumentException If the value is not an instance of Gettext\Translation
215
     *
216
     * @return Translation
217
     */
218
    public function offsetSet($index, $value)
219
    {
220
        if (!($value instanceof Translation)) {
221
            throw new InvalidArgumentException(
222
                'Only instances of Gettext\\Translation must be added to a Gettext\\Translations'
223
            );
224
        }
225
226
        $id = $value->getId();
227
228
        if ($this->offsetExists($id)) {
229
            $this[$id]->mergeWith($value);
230
231
            return $this[$id];
232
        }
233
234
        parent::offsetSet($id, $value);
235
236
        return $value;
237
    }
238
239
    /**
240
     * Set the plural definition.
241
     *
242
     * @param int    $count
243
     * @param string $rule
244
     *
245
     * @return self
246
     */
247
    public function setPluralForms($count, $rule)
248
    {
249
        if (preg_match('/[a-z]/i', str_replace('n', '', $rule))) {
250
            throw new \InvalidArgumentException('Invalid Plural form: ' . $rule);
251
        }
252
        $this->setHeader(self::HEADER_PLURAL, "nplurals={$count}; plural={$rule};");
253
254
        return $this;
255
    }
256
257
    /**
258
     * Returns the parsed plural definition.
259
     *
260
     * @param null|array [count, rule]
261
     */
262
    public function getPluralForms()
263
    {
264
        $header = $this->getHeader(self::HEADER_PLURAL);
265
266
        if (!empty($header)
267
            && preg_match('/^nplurals\s*=\s*(\d+)\s*;\s*plural\s*=\s*([^;]+)\s*;$/', $header, $matches)
268
        ) {
269
            return [intval($matches[1]), $matches[2]];
270
        }
271
    }
272
273
    /**
274
     * Set a new header.
275
     *
276
     * @param string $name
277
     * @param string $value
278
     *
279
     * @return self
280
     */
281
    public function setHeader($name, $value)
282
    {
283
        $name = trim($name);
284
        $this->headers[$name] = trim($value);
285
286
        return $this;
287
    }
288
289
    /**
290
     * Returns a header value.
291
     *
292
     * @param string $name
293
     *
294
     * @return null|string
295
     */
296
    public function getHeader($name)
297
    {
298
        return isset($this->headers[$name]) ? $this->headers[$name] : null;
299
    }
300
301
    /**
302
     * Returns all header for this translations (in alphabetic order).
303
     *
304
     * @return array
305
     */
306
    public function getHeaders()
307
    {
308
        if (static::$options['headersSorting']) {
309
            ksort($this->headers);
310
        }
311
312
        return $this->headers;
313
    }
314
315
    /**
316
     * Removes all headers.
317
     *
318
     * @return self
319
     */
320
    public function deleteHeaders()
321
    {
322
        $this->headers = [];
323
324
        return $this;
325
    }
326
327
    /**
328
     * Removes one header.
329
     *
330
     * @param string $name
331
     *
332
     * @return self
333
     */
334
    public function deleteHeader($name)
335
    {
336
        unset($this->headers[$name]);
337
338
        return $this;
339
    }
340
341
    /**
342
     * Returns the language value.
343
     *
344
     * @return string $language
345
     */
346
    public function getLanguage()
347
    {
348
        return $this->getHeader(self::HEADER_LANGUAGE);
349
    }
350
351
    /**
352
     * Sets the language and the plural forms.
353
     *
354
     * @param string $language
355
     *
356
     * @throws InvalidArgumentException if the language hasn't been recognized
357
     *
358
     * @return self
359
     */
360
    public function setLanguage($language)
361
    {
362
        $this->setHeader(self::HEADER_LANGUAGE, trim($language));
363
364
        if (($info = Language::getById($language))) {
365
            return $this->setPluralForms(count($info->categories), $info->formula);
366
        }
367
368
        throw new InvalidArgumentException(sprintf('The language "%s" is not valid', $language));
369
    }
370
371
    /**
372
     * Checks whether the language is empty or not.
373
     *
374
     * @return bool
375
     */
376
    public function hasLanguage()
377
    {
378
        $language = $this->getLanguage();
379
380
        return (is_string($language) && ($language !== '')) ? true : false;
381
    }
382
383
    /**
384
     * Set a new domain for this translations.
385
     *
386
     * @param string $domain
387
     *
388
     * @return self
389
     */
390
    public function setDomain($domain)
391
    {
392
        $this->setHeader(self::HEADER_DOMAIN, trim($domain));
393
394
        return $this;
395
    }
396
397
    /**
398
     * Returns the domain.
399
     *
400
     * @return string
401
     */
402
    public function getDomain()
403
    {
404
        return $this->getHeader(self::HEADER_DOMAIN);
405
    }
406
407
    /**
408
     * Checks whether the domain is empty or not.
409
     *
410
     * @return bool
411
     */
412
    public function hasDomain()
413
    {
414
        $domain = $this->getDomain();
415
416
        return (is_string($domain) && ($domain !== '')) ? true : false;
417
    }
418
419
    /**
420
     * Search for a specific translation.
421
     *
422
     * @param string|Translation $context  The context of the translation or a translation instance
423
     * @param string             $original The original string
424
     * @warning Translations with custom identifiers (e.g. XLIFF unit IDs) cannot be found using this function.
425
     *
426
     * @return Translation|false
427
     */
428
    public function find($context, $original = '')
429
    {
430
        if ($context instanceof Translation) {
431
            $id = $context->getId();
432
        } else {
433
            $id = Translation::generateId($context, $original);
434
        }
435
436
        return $this->offsetExists($id) ? $this[$id] : false;
437
    }
438
439
    /**
440
     * Count all elements translated
441
     *
442
     * @return integer
443
     */
444
    public function countTranslated()
445
    {
446
        $c = 0;
447
        foreach ($this as $v) {
448
            if ($v->hasTranslation()) {
449
                $c++;
450
            }
451
        }
452
        return $c;
453
    }
454
455
    /**
456
     * Creates and insert/merges a new translation.
457
     *
458
     * @param string $context  The translation context
459
     * @param string $original The translation original string
460
     * @param string $plural   The translation original plural string
461
     *
462
     * @return Translation The translation created
463
     */
464
    public function insert($context, $original, $plural = '')
465
    {
466
        return $this->offsetSet(null, $this->createNewTranslation($context, $original, $plural));
467
    }
468
469
    /**
470
     * Merges this translations with other translations.
471
     *
472
     * @param Translations $translations The translations instance to merge with
473
     * @param int          $options
474
     *
475
     * @return self
476
     */
477
    public function mergeWith(Translations $translations, $options = Merge::DEFAULTS)
478
    {
479
        Merge::mergeHeaders($translations, $this, $options);
480
        Merge::mergeTranslations($translations, $this, $options);
481
482
        return $this;
483
    }
484
485
    /**
486
     * Create a new instance of a Translation object.
487
     *
488
     * @param string $context  The context of the translation
489
     * @param string $original The original string
490
     * @param string $plural   The original plural string
491
     * @return Translation New Translation instance
492
     */
493
    public function createNewTranslation($context, $original, $plural = '')
494
    {
495
        $class = $this->translationClass;
496
        return $class::create($context, $original, $plural);
497
    }
498
}
499