briannesbitt / Carbon
1
<?php
2

3
/**
4
 * This file is part of the Carbon package.
5
 *
6
 * (c) Brian Nesbitt <brian@nesbot.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
namespace Carbon\Traits;
12

13
use Carbon\CarbonInterface;
14
use Carbon\Exceptions\InvalidTypeException;
15
use Carbon\Exceptions\NotLocaleAwareException;
16
use Carbon\Language;
17
use Carbon\Translator;
18
use Closure;
19
use Symfony\Component\Translation\TranslatorBagInterface;
20
use Symfony\Component\Translation\TranslatorInterface;
21
use Symfony\Contracts\Translation\LocaleAwareInterface;
22
use Symfony\Contracts\Translation\TranslatorInterface as ContractsTranslatorInterface;
23

24
if (!interface_exists('Symfony\\Component\\Translation\\TranslatorInterface')) {
25
    class_alias(
26
        'Symfony\\Contracts\\Translation\\TranslatorInterface',
27
        'Symfony\\Component\\Translation\\TranslatorInterface'
28
    );
29
}
30

31
/**
32
 * Trait Localization.
33
 *
34
 * Embed default and locale translators and translation base methods.
35
 */
36
trait Localization
37
{
38
    /**
39
     * Default translator.
40
     *
41
     * @var \Symfony\Component\Translation\TranslatorInterface
42
     */
43
    protected static $translator;
44

45
    /**
46
     * Specific translator of the current instance.
47
     *
48
     * @var \Symfony\Component\Translation\TranslatorInterface
49
     */
50
    protected $localTranslator;
51

52
    /**
53
     * Options for diffForHumans().
54
     *
55
     * @var int
56
     */
57
    protected static $humanDiffOptions = CarbonInterface::NO_ZERO_DIFF;
58

59
    /**
60
     * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
61
     *             You should rather use the ->settings() method.
62
     * @see settings
63
     *
64
     * @param int $humanDiffOptions
65
     */
66 2
    public static function setHumanDiffOptions($humanDiffOptions)
67
    {
68 2
        static::$humanDiffOptions = $humanDiffOptions;
69
    }
70

71
    /**
72
     * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
73
     *             You should rather use the ->settings() method.
74
     * @see settings
75
     *
76
     * @param int $humanDiffOption
77
     */
78 2
    public static function enableHumanDiffOption($humanDiffOption)
79
    {
80 2
        static::$humanDiffOptions = static::getHumanDiffOptions() | $humanDiffOption;
81
    }
82

83
    /**
84
     * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
85
     *             You should rather use the ->settings() method.
86
     * @see settings
87
     *
88
     * @param int $humanDiffOption
89
     */
90 2
    public static function disableHumanDiffOption($humanDiffOption)
91
    {
92 2
        static::$humanDiffOptions = static::getHumanDiffOptions() & ~$humanDiffOption;
93
    }
94

95
    /**
96
     * Return default humanDiff() options (merged flags as integer).
97
     *
98
     * @return int
99
     */
100 2
    public static function getHumanDiffOptions()
101
    {
102 2
        return static::$humanDiffOptions;
103
    }
104

105
    /**
106
     * Get the default translator instance in use.
107
     *
108
     * @return \Symfony\Component\Translation\TranslatorInterface
109
     */
110 2
    public static function getTranslator()
111
    {
112 2
        return static::translator();
113
    }
114

115
    /**
116
     * Set the default translator instance to use.
117
     *
118
     * @param \Symfony\Component\Translation\TranslatorInterface $translator
119
     *
120
     * @return void
121
     */
122 2
    public static function setTranslator(TranslatorInterface $translator)
123
    {
124 2
        static::$translator = $translator;
125
    }
126

127
    /**
128
     * Return true if the current instance has its own translator.
129
     *
130
     * @return bool
131
     */
132 2
    public function hasLocalTranslator()
133
    {
134 2
        return isset($this->localTranslator);
135
    }
136

137
    /**
138
     * Get the translator of the current instance or the default if none set.
139
     *
140
     * @return \Symfony\Component\Translation\TranslatorInterface
141
     */
142 2
    public function getLocalTranslator()
143
    {
144 2
        return $this->localTranslator ?: static::translator();
145
    }
146

147
    /**
148
     * Set the translator for the current instance.
149
     *
150
     * @param \Symfony\Component\Translation\TranslatorInterface $translator
151
     *
152
     * @return $this
153
     */
154 2
    public function setLocalTranslator(TranslatorInterface $translator)
155
    {
156 2
        $this->localTranslator = $translator;
157

158 2
        return $this;
159
    }
160

161
    /**
162
     * Returns raw translation message for a given key.
163
     *
164
     * @param \Symfony\Component\Translation\TranslatorInterface $translator the translator to use
165
     * @param string                                             $key        key to find
166
     * @param string|null                                        $locale     current locale used if null
167
     * @param string|null                                        $default    default value if translation returns the key
168
     *
169
     * @return string
170
     */
171 2
    public static function getTranslationMessageWith($translator, string $key, string $locale = null, string $default = null)
172
    {
173 2
        if (!($translator instanceof TranslatorBagInterface && $translator instanceof TranslatorInterface)) {
174 2
            throw new InvalidTypeException(
175
                'Translator does not implement '.TranslatorInterface::class.' and '.TranslatorBagInterface::class.'. '.
176 2
                (\is_object($translator) ? \get_class($translator) : \gettype($translator)).' has been given.'
177
            );
178
        }
179

180 2
        if (!$locale && $translator instanceof LocaleAwareInterface) {
181 2
            $locale = $translator->getLocale();
182
        }
183

184 2
        $result = $translator->getCatalogue($locale)->get($key);
185

186 2
        return $result === $key ? $default : $result;
187
    }
188

189
    /**
190
     * Returns raw translation message for a given key.
191
     *
192
     * @param string                                             $key        key to find
193
     * @param string|null                                        $locale     current locale used if null
194
     * @param string|null                                        $default    default value if translation returns the key
195
     * @param \Symfony\Component\Translation\TranslatorInterface $translator an optional translator to use
196
     *
197
     * @return string
198
     */
199 2
    public function getTranslationMessage(string $key, string $locale = null, string $default = null, $translator = null)
200
    {
201 2
        return static::getTranslationMessageWith($translator ?: $this->getLocalTranslator(), $key, $locale, $default);
202
    }
203

204
    /**
205
     * Translate using translation string or callback available.
206
     *
207
     * @param \Symfony\Component\Translation\TranslatorInterface $translator
208
     * @param string                                             $key
209
     * @param array                                              $parameters
210
     * @param null                                               $number
211
     *
212
     * @return string
213
     */
214 2
    public static function translateWith(TranslatorInterface $translator, string $key, array $parameters = [], $number = null): string
215
    {
216 2
        $message = static::getTranslationMessageWith($translator, $key, null, $key);
217 2
        if ($message instanceof Closure) {
218 2
            return (string) $message(...array_values($parameters));
219
        }
220

221 2
        if ($number !== null) {
222 2
            $parameters['%count%'] = $number;
223
        }
224 2
        if (isset($parameters['%count%'])) {
225 2
            $parameters[':count'] = $parameters['%count%'];
226
        }
227

228
        // @codeCoverageIgnoreStart
229
        $choice = $translator instanceof ContractsTranslatorInterface
230
            ? $translator->trans($key, $parameters)
231
            : $translator->transChoice($key, $number, $parameters);
232
        // @codeCoverageIgnoreEnd
233

234 2
        return (string) $choice;
235
    }
236

237
    /**
238
     * Translate using translation string or callback available.
239
     *
240
     * @param string                                             $key
241
     * @param array                                              $parameters
242
     * @param null                                               $number
243
     * @param \Symfony\Component\Translation\TranslatorInterface $translator
244
     *
245
     * @return string
246
     */
247 2
    public function translate(string $key, array $parameters = [], $number = null, TranslatorInterface $translator = null, bool $altNumbers = false): string
248
    {
249 2
        $translation = static::translateWith($translator ?: $this->getLocalTranslator(), $key, $parameters, $number);
250

251 2
        if ($number !== null && $altNumbers) {
252 2
            return str_replace($number, $this->translateNumber($number), $translation);
253
        }
254

255 2
        return $translation;
256
    }
257

258
    /**
259
     * Returns the alternative number for a given integer if available in the current locale.
260
     *
261
     * @param int $number
262
     *
263
     * @return string
264
     */
265 2
    public function translateNumber(int $number): string
266
    {
267 2
        $translateKey = "alt_numbers.$number";
268 2
        $symbol = $this->translate($translateKey);
269

270 2
        if ($symbol !== $translateKey) {
271 2
            return $symbol;
272
        }
273

274 2
        if ($number > 99 && $this->translate('alt_numbers.99') !== 'alt_numbers.99') {
275 2
            $start = '';
276 2
            foreach ([10000, 1000, 100] as $exp) {
277 2
                $key = "alt_numbers_pow.$exp";
278 2
                if ($number >= $exp && $number < $exp * 10 && ($pow = $this->translate($key)) !== $key) {
279 2
                    $unit = floor($number / $exp);
280 2
                    $number -= $unit * $exp;
281 2
                    $start .= ($unit > 1 ? $this->translate("alt_numbers.$unit") : '').$pow;
282
                }
283
            }
284 2
            $result = '';
285 2
            while ($number) {
286 2
                $chunk = $number % 100;
287 2
                $result = $this->translate("alt_numbers.$chunk").$result;
288 2
                $number = floor($number / 100);
289
            }
290

291 2
            return "$start$result";
292
        }
293

294 2
        if ($number > 9 && $this->translate('alt_numbers.9') !== 'alt_numbers.9') {
295 2
            $result = '';
296 2
            while ($number) {
297 2
                $chunk = $number % 10;
298 2
                $result = $this->translate("alt_numbers.$chunk").$result;
299 2
                $number = floor($number / 10);
300
            }
301

302 2
            return $result;
303
        }
304

305 2
        return "$number";
306
    }
307

308
    /**
309
     * Translate a time string from a locale to an other.
310
     *
311
     * @param string      $timeString date/time/duration string to translate (may also contain English)
312
     * @param string|null $from       input locale of the $timeString parameter (`Carbon::getLocale()` by default)
313
     * @param string|null $to         output locale of the result returned (`"en"` by default)
314
     * @param int         $mode       specify what to translate with options:
315
     *                                - CarbonInterface::TRANSLATE_ALL (default)
316
     *                                - CarbonInterface::TRANSLATE_MONTHS
317
     *                                - CarbonInterface::TRANSLATE_DAYS
318
     *                                - CarbonInterface::TRANSLATE_UNITS
319
     *                                - CarbonInterface::TRANSLATE_MERIDIEM
320
     *                                You can use pipe to group: CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS
321
     *
322
     * @return string
323
     */
324 2
    public static function translateTimeString($timeString, $from = null, $to = null, $mode = CarbonInterface::TRANSLATE_ALL)
325
    {
326
        // Fallback source and destination locales
327 2
        $from = $from ?: static::getLocale();
328 2
        $to = $to ?: 'en';
329

330 2
        if ($from === $to) {
331 2
            return $timeString;
332
        }
333

334
        // Standardize apostrophe
335 2
        $timeString = strtr($timeString, ['’' => "'"]);
336

337 2
        $fromTranslations = [];
338 2
        $toTranslations = [];
339

340 2
        foreach (['from', 'to'] as $key) {
341 2
            $language = $$key;
342 2
            $translator = Translator::get($language);
343 2
            $translations = $translator->getMessages();
344

345 2
            if (!isset($translations[$language])) {
346 2
                return $timeString;
347
            }
348

349 2
            $translationKey = $key.'Translations';
350 2
            $messages = $translations[$language];
351 2
            $months = $messages['months'] ?? [];
352 2
            $weekdays = $messages['weekdays'] ?? [];
353 2
            $meridiem = $messages['meridiem'] ?? ['AM', 'PM'];
354

355 2
            if ($key === 'from') {
356 2
                foreach (['months', 'weekdays'] as $variable) {
357 2
                    $list = $messages[$variable.'_standalone'] ?? null;
358

359 2
                    if ($list) {
360 2
                        foreach ($$variable as $index => &$name) {
361 2
                            $name .= '|'.$messages[$variable.'_standalone'][$index];
362
                        }
363
                    }
364
                }
365
            }
366

367 2
            $$translationKey = array_merge(
368 2
                $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($months, 12, $timeString) : [],
369 2
                $mode & CarbonInterface::TRANSLATE_MONTHS ? static::getTranslationArray($messages['months_short'] ?? [], 12, $timeString) : [],
370 2
                $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($weekdays, 7, $timeString) : [],
371 2
                $mode & CarbonInterface::TRANSLATE_DAYS ? static::getTranslationArray($messages['weekdays_short'] ?? [], 7, $timeString) : [],
372 2
                $mode & CarbonInterface::TRANSLATE_DIFF ? static::translateWordsByKeys([
373 2
                    'diff_now',
374
                    'diff_today',
375
                    'diff_yesterday',
376
                    'diff_tomorrow',
377
                    'diff_before_yesterday',
378
                    'diff_after_tomorrow',
379 2
                ], $messages, $key) : [],
380 2
                $mode & CarbonInterface::TRANSLATE_UNITS ? static::translateWordsByKeys([
381 2
                    'year',
382
                    'month',
383
                    'week',
384
                    'day',
385
                    'hour',
386
                    'minute',
387
                    'second',
388 2
                ], $messages, $key) : [],
389
                $mode & CarbonInterface::TRANSLATE_MERIDIEM ? array_map(function ($hour) use ($meridiem) {
390 2
                    if (\is_array($meridiem)) {
391 2
                        return $meridiem[$hour < 12 ? 0 : 1];
392
                    }
393

394 2
                    return $meridiem($hour, 0, false);
395 2
                }, range(0, 23)) : []
396
            );
397
        }
398

399
        return substr(preg_replace_callback('/(?<=[\d\s+.\/,_-])('.implode('|', $fromTranslations).')(?=[\d\s+.\/,_-])/iu', function ($match) use ($fromTranslations, $toTranslations) {
400 2
            [$chunk] = $match;
401

402 2
            foreach ($fromTranslations as $index => $word) {
403 2
                if (preg_match("/^$word\$/iu", $chunk)) {
404 2
                    return $toTranslations[$index] ?? '';
405
                }
406
            }
407

408
            return $chunk; // @codeCoverageIgnore
409 2
        }, " $timeString "), 1, -1);
410
    }
411

412
    /**
413
     * Translate a time string from the current locale (`$date->locale()`) to an other.
414
     *
415
     * @param string      $timeString time string to translate
416
     * @param string|null $to         output locale of the result returned ("en" by default)
417
     *
418
     * @return string
419
     */
420 2
    public function translateTimeStringTo($timeString, $to = null)
421
    {
422 2
        return static::translateTimeString($timeString, $this->getTranslatorLocale(), $to);
423
    }
424

425
    /**
426
     * Get/set the locale for the current instance.
427
     *
428
     * @param string|null $locale
429
     * @param string      ...$fallbackLocales
430
     *
431
     * @return $this|string
432
     */
433 2
    public function locale(string $locale = null, ...$fallbackLocales)
434
    {
435 2
        if ($locale === null) {
436 2
            return $this->getTranslatorLocale();
437
        }
438

439 2
        if (!$this->localTranslator || $this->getTranslatorLocale($this->localTranslator) !== $locale) {
440 2
            $translator = Translator::get($locale);
441

442 2
            if (!empty($fallbackLocales)) {
443 2
                $translator->setFallbackLocales($fallbackLocales);
444

445 2
                foreach ($fallbackLocales as $fallbackLocale) {
446 2
                    $messages = Translator::get($fallbackLocale)->getMessages();
447

448 2
                    if (isset($messages[$fallbackLocale])) {
449 2
                        $translator->setMessages($fallbackLocale, $messages[$fallbackLocale]);
450
                    }
451
                }
452
            }
453

454 2
            $this->setLocalTranslator($translator);
455
        }
456

457 2
        return $this;
458
    }
459

460
    /**
461
     * Get the current translator locale.
462
     *
463
     * @return string
464
     */
465 2
    public static function getLocale()
466
    {
467 2
        return static::getLocaleAwareTranslator()->getLocale();
468
    }
469

470
    /**
471
     * Set the current translator locale and indicate if the source locale file exists.
472
     * Pass 'auto' as locale to use closest language from the current LC_TIME locale.
473
     *
474
     * @param string $locale locale ex. en
475
     *
476
     * @return bool
477
     */
478 2
    public static function setLocale($locale)
479
    {
480 2
        return static::getLocaleAwareTranslator()->setLocale($locale) !== false;
481
    }
482

483
    /**
484
     * Set the fallback locale.
485
     *
486
     * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
487
     *
488
     * @param string $locale
489
     */
490 2
    public static function setFallbackLocale($locale)
491
    {
492 2
        $translator = static::getTranslator();
493

494 2
        if (method_exists($translator, 'setFallbackLocales')) {
495 2
            $translator->setFallbackLocales([$locale]);
496

497 2
            if ($translator instanceof Translator) {
498 2
                $preferredLocale = $translator->getLocale();
499 2
                $translator->setMessages($preferredLocale, array_replace_recursive(
500 2
                    $translator->getMessages()[$locale] ?? [],
501 2
                    Translator::get($locale)->getMessages()[$locale] ?? [],
502 2
                    $translator->getMessages($preferredLocale)
503
                ));
504
            }
505
        }
506
    }
507

508
    /**
509
     * Get the fallback locale.
510
     *
511
     * @see https://symfony.com/doc/current/components/translation.html#fallback-locales
512
     *
513
     * @return string|null
514
     */
515 2
    public static function getFallbackLocale()
516
    {
517 2
        $translator = static::getTranslator();
518

519 2
        if (method_exists($translator, 'getFallbackLocales')) {
520 2
            return $translator->getFallbackLocales()[0] ?? null;
521
        }
522

523 2
        return null;
524
    }
525

526
    /**
527
     * Set the current locale to the given, execute the passed function, reset the locale to previous one,
528
     * then return the result of the closure (or null if the closure was void).
529
     *
530
     * @param string   $locale locale ex. en
531
     * @param callable $func
532
     *
533
     * @return mixed
534
     */
535 2
    public static function executeWithLocale($locale, $func)
536
    {
537 2
        $currentLocale = static::getLocale();
538 2
        $result = $func(static::setLocale($locale) ? static::getLocale() : false, static::translator());
539 2
        static::setLocale($currentLocale);
540

541 2
        return $result;
542
    }
543

544
    /**
545
     * Returns true if the given locale is internally supported and has short-units support.
546
     * Support is considered enabled if either year, day or hour has a short variant translated.
547
     *
548
     * @param string $locale locale ex. en
549
     *
550
     * @return bool
551
     */
552 2
    public static function localeHasShortUnits($locale)
553
    {
554
        return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
555 2
            return $newLocale &&
556
                (
557 2
                    ($y = static::translateWith($translator, 'y')) !== 'y' &&
558 2
                    $y !== static::translateWith($translator, 'year')
559
                ) || (
560 2
                    ($y = static::translateWith($translator, 'd')) !== 'd' &&
561 2
                    $y !== static::translateWith($translator, 'day')
562
                ) || (
563 2
                    ($y = static::translateWith($translator, 'h')) !== 'h' &&
564 2
                    $y !== static::translateWith($translator, 'hour')
565
                );
566 2
        });
567
    }
568

569
    /**
570
     * Returns true if the given locale is internally supported and has diff syntax support (ago, from now, before, after).
571
     * Support is considered enabled if the 4 sentences are translated in the given locale.
572
     *
573
     * @param string $locale locale ex. en
574
     *
575
     * @return bool
576
     */
577 2
    public static function localeHasDiffSyntax($locale)
578
    {
579
        return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
580 2
            if (!$newLocale) {
581 2
                return false;
582
            }
583

584 2
            foreach (['ago', 'from_now', 'before', 'after'] as $key) {
585 2
                if ($translator instanceof TranslatorBagInterface && $translator->getCatalogue($newLocale)->get($key) instanceof Closure) {
586 2
                    continue;
587
                }
588

589 2
                if ($translator->trans($key) === $key) {
590 2
                    return false;
591
                }
592
            }
593

594 2
            return true;
595 2
        });
596
    }
597

598
    /**
599
     * Returns true if the given locale is internally supported and has words for 1-day diff (just now, yesterday, tomorrow).
600
     * Support is considered enabled if the 3 words are translated in the given locale.
601
     *
602
     * @param string $locale locale ex. en
603
     *
604
     * @return bool
605
     */
606 2
    public static function localeHasDiffOneDayWords($locale)
607
    {
608
        return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
609 2
            return $newLocale &&
610 2
                $translator->trans('diff_now') !== 'diff_now' &&
611 2
                $translator->trans('diff_yesterday') !== 'diff_yesterday' &&
612 2
                $translator->trans('diff_tomorrow') !== 'diff_tomorrow';
613 2
        });
614
    }
615

616
    /**
617
     * Returns true if the given locale is internally supported and has words for 2-days diff (before yesterday, after tomorrow).
618
     * Support is considered enabled if the 2 words are translated in the given locale.
619
     *
620
     * @param string $locale locale ex. en
621
     *
622
     * @return bool
623
     */
624 2
    public static function localeHasDiffTwoDayWords($locale)
625
    {
626
        return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
627 2
            return $newLocale &&
628 2
                $translator->trans('diff_before_yesterday') !== 'diff_before_yesterday' &&
629 2
                $translator->trans('diff_after_tomorrow') !== 'diff_after_tomorrow';
630 2
        });
631
    }
632

633
    /**
634
     * Returns true if the given locale is internally supported and has period syntax support (X times, every X, from X, to X).
635
     * Support is considered enabled if the 4 sentences are translated in the given locale.
636
     *
637
     * @param string $locale locale ex. en
638
     *
639
     * @return bool
640
     */
641 2
    public static function localeHasPeriodSyntax($locale)
642
    {
643
        return static::executeWithLocale($locale, function ($newLocale, TranslatorInterface $translator) {
644 2
            return $newLocale &&
645 2
                $translator->trans('period_recurrences') !== 'period_recurrences' &&
646 2
                $translator->trans('period_interval') !== 'period_interval' &&
647 2
                $translator->trans('period_start_date') !== 'period_start_date' &&
648 2
                $translator->trans('period_end_date') !== 'period_end_date';
649 2
        });
650
    }
651

652
    /**
653
     * Returns the list of internally available locales and already loaded custom locales.
654
     * (It will ignore custom translator dynamic loading.)
655
     *
656
     * @return array
657
     */
658 2
    public static function getAvailableLocales()
659
    {
660 2
        $translator = static::getLocaleAwareTranslator();
661

662 2
        return $translator instanceof Translator
663 2
            ? $translator->getAvailableLocales()
664 2
            : [$translator->getLocale()];
665
    }
666

667
    /**
668
     * Returns list of Language object for each available locale. This object allow you to get the ISO name, native
669
     * name, region and variant of the locale.
670
     *
671
     * @return Language[]
672
     */
673 2
    public static function getAvailableLocalesInfo()
674
    {
675 2
        $languages = [];
676 2
        foreach (static::getAvailableLocales() as $id) {
677 2
            $languages[$id] = new Language($id);
678
        }
679

680 2
        return $languages;
681
    }
682

683
    /**
684
     * Initialize the default translator instance if necessary.
685
     *
686
     * @return \Symfony\Component\Translation\TranslatorInterface
687
     */
688 2
    protected static function translator()
689
    {
690 2
        if (static::$translator === null) {
691 2
            static::$translator = Translator::get();
692
        }
693

694 2
        return static::$translator;
695
    }
696

697
    /**
698
     * Get the locale of a given translator.
699
     *
700
     * If null or omitted, current local translator is used.
701
     * If no local translator is in use, current global translator is used.
702
     *
703
     * @param null $translator
704
     *
705
     * @return string|null
706
     */
707 2
    protected function getTranslatorLocale($translator = null): ?string
708
    {
709 2
        if (\func_num_args() === 0) {
710 2
            $translator = $this->getLocalTranslator();
711
        }
712

713 2
        $translator = static::getLocaleAwareTranslator($translator);
714

715 2
        return $translator ? $translator->getLocale() : null;
716
    }
717

718
    /**
719
     * Throw an error if passed object is not LocaleAwareInterface.
720
     *
721
     * @param LocaleAwareInterface|null $translator
722
     *
723
     * @return LocaleAwareInterface|null
724
     */
725 2
    protected static function getLocaleAwareTranslator($translator = null)
726
    {
727 2
        if (\func_num_args() === 0) {
728 2
            $translator = static::translator();
729
        }
730

731 2
        if ($translator && !($translator instanceof LocaleAwareInterface || method_exists($translator, 'getLocale'))) {
732 2
            throw new NotLocaleAwareException($translator);
733
        }
734

735 2
        return $translator;
736
    }
737

738
    /**
739
     * Return the word cleaned from its translation codes.
740
     *
741
     * @param string $word
742
     *
743
     * @return string
744
     */
745 2
    private static function cleanWordFromTranslationString($word)
746
    {
747 2
        $word = str_replace([':count', '%count', ':time'], '', $word);
748 2
        $word = strtr($word, ['’' => "'"]);
749 2
        $word = preg_replace('/({\d+(,(\d+|Inf))?}|[\[\]]\d+(,(\d+|Inf))?[\[\]])/', '', $word);
750

751 2
        return trim($word);
752
    }
753

754
    /**
755
     * Translate a list of words.
756
     *
757
     * @param string[] $keys     keys to translate.
758
     * @param string[] $messages messages bag handling translations.
759
     * @param string   $key      'to' (to get the translation) or 'from' (to get the detection RegExp pattern).
760
     *
761
     * @return string[]
762
     */
763 2
    private static function translateWordsByKeys($keys, $messages, $key): array
764
    {
765
        return array_map(function ($wordKey) use ($messages, $key) {
766 2
            $message = $key === 'from' && isset($messages[$wordKey.'_regexp'])
767 2
                ? $messages[$wordKey.'_regexp']
768 2
                : ($messages[$wordKey] ?? null);
769

770 2
            if (!$message) {
771 2
                return '>>DO NOT REPLACE<<';
772
            }
773

774 2
            $parts = explode('|', $message);
775

776 2
            return $key === 'to'
777 2
                ? static::cleanWordFromTranslationString(end($parts))
778 2
                : '(?:'.implode('|', array_map([static::class, 'cleanWordFromTranslationString'], $parts)).')';
779 2
        }, $keys);
780
    }
781

782
    /**
783
     * Get an array of translations based on the current date.
784
     *
785
     * @param callable $translation
786
     * @param int      $length
787
     * @param string   $timeString
788
     *
789
     * @return string[]
790
     */
791 2
    private static function getTranslationArray($translation, $length, $timeString): array
792
    {
793 2
        $filler = '>>DO NOT REPLACE<<';
794

795 2
        if (\is_array($translation)) {
796 2
            return array_pad($translation, $length, $filler);
797
        }
798

799 2
        $list = [];
800 2
        $date = static::now();
801

802 2
        for ($i = 0; $i < $length; $i++) {
803 2
            $list[] = $translation($date, $timeString, $i) ?? $filler;
804
        }
805

806 2
        return $list;
807
    }
808
}

Read our documentation on viewing source code .

Loading