Completed
Pull Request — master (#136)
by Maxime
01:47
created

Translations::countTranslated()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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