Passed
Branch master (b3bcb6)
by Alexander
02:46
created

Translator   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 343
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 47
dl 0
loc 343
rs 8.439
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A hasIdentifier() 0 3 1
C addMissingIdentifierToDB() 0 27 7
A __construct() 0 5 1
A replaceParameter() 0 7 2
A returnMissingTranslation() 0 6 2
A getAllTranslations() 0 7 2
B log() 0 19 5
A createHandler() 0 20 3
B validateLocale() 0 22 6
C translate() 0 42 8
A addLiveModeLink() 0 9 1
A setLocale() 0 9 2
A getConfigValue() 0 3 1
B guessLocale() 0 25 6

How to fix   Complexity   

Complex Class

Complex classes like Translator 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.

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 Translator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Translator
5
 */
6
namespace Hokan22\LaravelTranslator;
7
8
use Hokan22\LaravelTranslator\Handler\DatabaseHandler;
9
use Hokan22\LaravelTranslator\Handler\DefaultHandler;
10
use Hokan22\LaravelTranslator\Handler\TranslationNotFoundException;
11
use Hokan22\LaravelTranslator\Models\TranslationIdentifier;
12
use Illuminate\Support\Facades\Config;
13
use Illuminate\Support\Facades\Log;
14
use Symfony\Component\Translation\Exception\NotFoundResourceException;
15
16
17
/**
18
 * Class Translator
19
 *
20
 * @package  Hokan22\LaravelTranslator
21
 * @author   Alexander Viertel <[email protected]>
22
 * @license  http://opensource.org/licenses/MIT MIT
23
 */
24
class Translator
25
{
26
    /** @var array|HandlerInterface[] $aHandler Class to Handle the Translation defined in config */
27
    protected $aHandler = [];
28
    /** @var string $locale The locale to translate to  */
29
    protected $locale = '';
30
    /** @var string $group The translation group */
31
    protected $group = 'default';
32
    /** @var string $configName The name of the config file */
33
    protected $configName = 'translator';
34
    /** @var array $config Config cache */
35
    protected $config;
36
37
    /**
38
     * Translator constructor.
39
     *
40
     * @param string $locale The locale to translate to
41
     * @throws \Exception
42
     */
43
    public function __construct($locale = '')
44
    {
45
        $this->config = Config::get($this->configName);
46
47
        $this->setLocale($locale);
48
    }
49
50
    /**
51
     * Return the config value for given key
52
     *
53
     * @param string $key Key for the config value to get
54
     * @return string|array Config value for $key
55
     */
56
    public function getConfigValue($key)
57
    {
58
        return $this->config[$key];
59
    }
60
61
    /**
62
     * Actual translate function
63
     * $parameter and $locale are optional
64
     *
65
     * @param string $identifier The identifier of the translation
66
     * @param array|null $parameters The parameters to inject into the translation
67
     * @param string $locale The locale to which to translate to overrides the class location for one translation
68
     * @throws \Exception
69
     * @return string Returns the translation with replaced parameters
70
     *
71
     */
72
    public function translate($identifier, $parameters = null, $locale = null)
73
    {
74
        if ($locale !== null) {
75
            $locale = $this->validateLocale($locale);
76
        } else {
77
            $locale = $this->locale;
78
        }
79
80
        if (!isset($this->aHandler[$locale])) {
81
            $this->aHandler[$locale] = $this->createHandler($locale);
82
        }
83
84
        try {
85
            $translation = $this->aHandler[$locale]->getTranslation($identifier, $this->group);
86
        } catch (NotFoundResourceException $exception) {
87
            // Thrown when the Identifier wasn't found
88
            $this->log($exception, 'error');
89
90
            if ($this->config['listening_enabled'] === true) {
91
                $this->addMissingIdentifierToDB($identifier, $parameters, 'default');
92
            }
93
94
            $translation = $this->returnMissingTranslation($identifier, $locale);
95
96
        } catch (TranslationNotFoundException $exception) {
97
            // Thrown when no translation for the locale was found
98
            $this->log($exception, 'error');
99
100
            $translation = $this->returnMissingTranslation($identifier, $locale);
101
        }
102
103
        if (is_array($parameters)) {
104
            $translation = $this->replaceParameter($translation, $parameters);
105
        }
106
107
        if (session('translation_live_mode')) {
108
            $id = $this->aHandler[$locale]->getDatabaseID($identifier);
109
            $translation = $this->addLiveModeLink($translation, $id);
110
        }
111
112
        // Return the translation
113
        return $translation;
114
    }
115
116
    /**
117
     * Inject a Link to the edit page into the translation
118
     *
119
     * @param string $translation
120
     * @param integer $id
121
     * @return string
122
     */
123
    public function addLiveModeLink($translation, $id) {
124
125
        $route = route('translator.admin.edit', ['id' => $id]);
126
127
        $inject = "<translation-anchor onclick='window.open(\"$route\", \"_blank\")' style='position: absolute; z-index: 999; cursor: pointer;'>&#9875;</translation-anchor>";
128
129
        $translation = "$translation $inject";
130
131
        return $translation;
132
    }
133
134
     /**
135
      * Sets the Handler
136
      *
137
      * @param $locale
138
      * @return DefaultHandler
139
      */
140
    protected function createHandler($locale)
141
    {
142
        $handler_class = $this->config['handler'];
143
144
        if (session('translation_live_mode')) {
145
            $handler_class = DatabaseHandler::class;
146
        }
147
148
        try {
149
            $oHandler = new $handler_class($locale);
150
151
        } catch (\Exception $exception) {
152
153
            $this->log($exception, 'error');
154
            $this->log('Falling back to DatabaseHandler', 'warning');
155
156
            $oHandler = new DefaultHandler($locale);
157
        }
158
159
        return $oHandler;
160
    }
161
162
    /**
163
     * Set the locale to use in translations
164
     *
165
     * @param string $locale The locale to use
166
     */
167
    public function setLocale($locale)
168
    {
169
        $locale = $this->validateLocale($locale);
170
171
        if (!isset($this->aHandler[$locale])) {
172
            $this->aHandler[$locale] = $this->createHandler($locale);
173
        }
174
175
        $this->locale = $locale;
176
    }
177
178
    /**
179
     * Add the missing identifier to the texts table in the database
180
     *
181
     * @param string $identifier The identifier to add to the db
182
     * @param array $parameters The parameters available for the translation
183
     * @param string $group The group to put the identifier in
184
     */
185
    public function addMissingIdentifierToDB($identifier, $parameters, $group)
186
    {
187
        if (!$this->hasIdentifier($identifier)) {
188
189
            $keys = [];
190
            if (is_array($parameters)) {
0 ignored issues
show
introduced by
The condition is_array($parameters) is always true.
Loading history...
191
                foreach($parameters as $key => $value) {
192
                    $keys[] = $key;
193
                }
194
            }
195
196
            TranslationIdentifier::create(
197
                [
198
                    "identifier"    => $identifier,
199
                    "parameters"    => $keys,
200
                    "group"         => isset($group) ? $group : 'default',
201
                    "page_name"     => app()->runningInConsole() ? '' : substr(request()->getRequestUri(), 1),
0 ignored issues
show
introduced by
The method runningInConsole() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

201
                    "page_name"     => app()->/** @scrutinizer ignore-call */ runningInConsole() ? '' : substr(request()->getRequestUri(), 1),
Loading history...
202
                ]
203
            );
204
205
            if (isset($this->aHandler[$this->locale])) {
206
                // When using file Cache, adding the Identifier to the Database will not add it to file Cache!
207
                $this->aHandler[$this->locale]->refreshCache();
208
            }
209
            $this->log('The translation string "'.$identifier.'" will be written to the Database', 'notice');
210
        } else {
211
            $this->log('The translation string "'.$identifier.'" is already in the Database!', 'warning');
212
        }
213
    }
214
215
    /**
216
     * Check if the identifier exists in the database
217
     *
218
     * @param string $identifier The identifier to check
219
     * @return boolean Returns true if the identifier was found
220
     */
221
    public function hasIdentifier($identifier)
222
    {
223
        return TranslationIdentifier::where('identifier', $identifier)->count() > 0;
224
    }
225
226
    /**
227
     * Replace the parameters in the translation
228
     *
229
     * @param string $translation The translation with the parameter tags
230
     * @param array $parameters The parameters which to inject in the translation
231
     * @return string Returns the translation which its parameters replaced
232
     *
233
     * @todo Make Prefix and Suffix configurable
234
     */
235
    protected function replaceParameter($translation, $parameters)
236
    {
237
        foreach ($parameters as $key => $parameter) {
238
            // If the string (e.g "{name}") is not specified within the "parameters" array it won't be replaced!
239
            $translation = str_replace("{".$key."}", $parameter, $translation);
240
        }
241
        return $translation;
242
    }
243
244
    /**
245
     * Return the translation identifier and the locale
246
     *
247
     * @param string $identifier The identifier which is missing
248
     * @param string $locale The locale of which the translation is missing
249
     * @throws \Exception
250
     * @return string The string to display instead of the translation
251
     */
252
    protected function returnMissingTranslation($identifier, $locale)
253
    {
254
        if (config('app.env') !== 'production') {
255
            return '&lt;'.$identifier.':'.$locale.'&gt;';
256
        }
257
        return $identifier;
258
    }
259
260
    /**
261
     * Checks if the given locale is in the available_locales array.
262
     * If not try to guess it or fall back to the default locale
263
     *
264
     * @param string $locale The locale to validate
265
     * @return string Returns the validated Locale
266
     */
267
    public function validateLocale($locale)
268
    {
269
        $avail_locales      = $this->config['available_locales'];
270
        $default_locale     = $this->config['default_locale'];
271
272
        if ($this->locale == $locale && $this->locale !== '') {
273
            return $locale;
274
        }
275
276
        if ($locale == null) {
277
            if (session()->get('locale') != '') {
278
                $locale = session()->get('locale');
279
            } else {
280
                return $default_locale;
281
            }
282
        }
283
284
        if (!in_array($locale, $avail_locales)) {
285
            $locale = $this->guessLocale($locale);
286
        }
287
288
        return $locale;
289
    }
290
291
    /**
292
     * Tries to match the locale to an available Locale
293
     * Else returns the default locale
294
     *
295
     * @param string $locale The locale to match
296
     * @throws NotFoundResourceException
297
     * @return string Returns the guessed Locale
298
     */
299
    private function guessLocale($locale)
300
    {
301
        $avail_locales      = $this->config['available_locales'];
302
        $default_locale     = $this->config['default_locale'];
303
304
        $found_locales = [];
305
306
        foreach ($avail_locales as $avail_locale) {
307
            if (strpos($avail_locale, $locale) !== false){
308
                $found_locales[] = $avail_locale;
309
            }
310
        }
311
312
        if (in_array($default_locale, $found_locales) || count($found_locales) == 0){
313
            $message = 'Locale "'.$locale.'" was not found! Falling back to default locale "'.$default_locale.'"';
314
            $locale = $default_locale;
315
316
        } else {
317
            $message = 'Locale "'.$locale.'" was not found! Falling back to similar locale "'.$found_locales[0].'"';
318
            $locale = $found_locales[0];
319
        }
320
321
        if ($message !== '') $this->log($message, 'warning');
322
323
        return $locale;
324
    }
325
326
    /**
327
     * Returns all translation for the the given locale of the given group
328
     *
329
     * @param string $locale The locale of the translations to get
330
     * @param string $group The group of the translations to get
331
     * @return array|mixed Returns an array of all translation in the $locale from group $group
332
     */
333
    public function getAllTranslations($locale, $group)
334
    {
335
        if (!isset($this->aHandler[$locale])) {
336
            $this->aHandler[$locale] = $this->createHandler($locale);
337
        }
338
339
        return $this->aHandler[$locale]->getAllTranslations($group);
340
    }
341
342
    /**
343
     * Log the given exception or string $exception as type $log_type when config log_level is set to debug
344
     *
345
     * @param \Exception|string $exception The Exception to log
346
     * @param string $log_type The type of the log to write in the log file
347
     */
348
    protected function log($exception, $log_type = 'notice')
349
    {
350
        if ($this->config['log_level'] !== 'debug') {
351
            return;
352
        }
353
354
        switch ($log_type) {
355
            case 'error':
356
                Log::error($exception);
357
                break;
358
359
            case 'warning':
360
                Log::warning($exception);
361
                break;
362
363
            case 'notice':
364
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
365
                Log::notice($exception);
366
                break;
367
        }
368
    }
369
}