Passed
Push — master ( 201e2f...675852 )
by Radovan
01:55
created

Translator::translate()   B

Complexity

Conditions 11
Paths 17

Size

Total Lines 50
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 11
eloc 27
c 2
b 0
f 1
nc 17
nop 2
dl 0
loc 50
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * BCKP Translator
5
 * (c) Radovan Kepák
6
 *
7
 * For the full copyright and license information, please view
8
 * the file license.md that was distributed with this source code.
9
 *
10
 * @author Radovan Kepak <[email protected]>
11
 */
12
13
declare(strict_types=1);
14
15
namespace Bckp\Translator;
16
17
use function array_key_exists;
18
use function end;
19
use function gettype;
20
use function is_array;
21
use function is_object;
22
use function is_string;
23
use function key;
24
use function method_exists;
25
use function vsprintf;
26
27
/**
28
 * Class Translator
29
 *
30
 * @package Bckp\Translator
31
 */
32
class Translator implements ITranslator
33
{
34
    /** @var ICatalogue */
35
    private $catalogue;
36
37
    /** @var IDiagnostics|null */
38
    private $diagnostics;
39
40
    /** @var callable function(string $string): string */
41
    private $normalizeCallback;
42
43
    /**
44
     * Translator constructor.
45
     *
46
     * @param ICatalogue $catalogue
47
     * @param IDiagnostics|null $diagnostics
48
     */
49
    public function __construct(ICatalogue $catalogue, IDiagnostics $diagnostics = null)
50
    {
51
        $this->catalogue = $catalogue;
52
        $this->normalizeCallback = [$this, 'normalize'];
53
        if ($this->diagnostics = $diagnostics) {
54
            $this->diagnostics->setLocale($catalogue->locale());
55
        }
56
    }
57
58
    /**
59
     * Normalize string to preserve frameworks placeholders
60
     *
61
     * @param string $string
62
     * @return string
63
     */
64
    public function normalize(string $string): string
65
    {
66
        return (string)str_replace(['%label', '%value', '%name'], ['%%label', '%%value', '%%name'], $string);
67
    }
68
69
    /**
70
     * @param callable $callback
71
     * @return void
72
     */
73
    public function setNormalizeCallback(callable $callback): void
74
    {
75
        $this->normalizeCallback = $callback;
76
    }
77
78
    /**
79
     * @param mixed $message
80
     * @param mixed ...$parameters
81
     * @return string
82
     */
83
    public function translate($message, ...$parameters): string
84
    {
85
        // html and empty are returned without processing
86
        if (empty($message)) {
87
            return (string)$message;
88
        }
89
90
        // expand parameters
91
        if (empty($parameters) && is_array($message) && is_numeric($message[1] ?? null)) {
92
            $parameters[] = $message[1];
93
        }
94
95
        // get message and plural if any
96
        $form = null;
97
        $message = $this->getMessage($message, $form);
98
        // check message to be string
99
        if (!is_string($message)) {
100
            return $this->warn('Expected string|array|object::__toString, but %s given.', gettype($message));
101
        }
102
103
        // process plural if any
104
        $result = $message;
105
        if ($translation = $this->catalogue->get($message)) {
106
            // plural
107
            if (is_array($translation)) {
108
                if (!array_key_exists($form, $translation) || $form === null) {
109
                    $this->warn(
110
                        'Plural form not defined. (message: %s, form: %s)',
111
                        (string)$message,
112
                        (string)$form
113
                    );
114
                    end($translation);
115
                    $form = key($translation);
116
                }
117
118
                $result = $translation[$form];
119
            } else {
120
                $result = $translation;
121
            }
122
123
            if ($parameters) {
124
                $result = ($this->normalizeCallback)($result);
125
                $result = @vsprintf($result, $parameters);
126
                // Intentionally @ as argument count can mismatch
127
            }
128
        } else {
129
            $this->untranslated((string)$message);
130
        }
131
132
        return $result;
133
    }
134
135
    /**
136
     * @param mixed $message
137
     * @param string|null $plural
138
     * @return string|null
139
     */
140
    protected function getMessage($message, ?string &$plural): ?string
141
    {
142
        if (is_string($message)) {
143
            return $message;
144
        }
145
146
        if (is_array($message) && is_string($message[0])) {
147
            $plural = $this->catalogue->plural((int)$message[1] ?? 1);
148
            return $message[0];
149
        }
150
151
        if (is_object($message) && method_exists($message, '__toString')) {
152
            return (string)$message;
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * @param string $message
160
     */
161
    protected function untranslated(string $message): void
162
    {
163
        if ($this->diagnostics !== null) {
164
            $this->diagnostics->untranslated($message);
165
        }
166
    }
167
168
    /**
169
     * @param string $message
170
     * @param mixed ...$parameters
171
     * @return string
172
     */
173
    protected function warn(string $message, ...$parameters): string
174
    {
175
        if (!empty($parameters)) {
176
            $message = @vsprintf($message, $parameters);
177
        } // Intentionally @ as parameter count can mismatch
178
179
        if ($this->diagnostics !== null) {
180
            $this->diagnostics->warning($message);
181
        }
182
183
        return $message;
184
    }
185
}
186