Completed
Push — master ( 488702...18fedf )
by Stéphane
22:05
created

I18N   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 496
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 39.08%

Importance

Changes 0
Metric Value
dl 0
loc 496
ccs 68
cts 174
cp 0.3908
rs 6
c 0
b 0
f 0
wmc 55
lcom 2
cbo 7

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 30 2
A getFilePath() 0 3 1
B getDefaultLanguage() 0 39 8
A setCurrentLanguage() 0 12 2
A loadLanguage() 0 18 3
A getCurrent() 0 4 1
A getCurrentId() 0 4 1
B setLanguage() 0 25 6
A isLoaded() 0 4 1
A isDefault() 0 8 3
A isAvailable() 0 4 1
A languages() 0 20 5
A languagesForSelect() 0 9 2
A translateGetString() 0 13 2
A translateInsertString() 0 18 1
B translate() 0 35 6
A getRawStrings() 0 4 1
A getContext() 0 23 5
A generate() 0 23 4

How to fix   Complexity   

Complex Class

Complex classes like I18N 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 I18N, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Translation management
5
 */
6
namespace Rocket\Translation;
7
8
use Exception;
9
use Illuminate\Contracts\Cache\Repository as CacheRepository;
10
use Illuminate\Contracts\Config\Repository as ConfigRepository;
11
use Illuminate\Contracts\Foundation\Application;
12
use Illuminate\Contracts\Session\Session;
13
use Illuminate\Contracts\Logging\Log;
14
use Request;
15
use Rocket\Translation\Model\Language;
16
use Rocket\Translation\Model\StringModel;
17
use Rocket\Translation\Model\Translation;
18
19
/**
20
 * Class I18N
21
 */
22
class I18N implements I18NInterface
23
{
24
    /**
25
     * An array of the loaded languages
26
     * @var array
27
     */
28
    protected $languagesLoaded = [];
29
30
    /**
31
     * An array of existing languages by ISO
32
     * @var array
33
     */
34
    protected $languagesIso = [];
35
36
    /**
37
     * An array of existing languages by ID
38
     * @var array
39
     */
40
    protected $languagesId = [];
41
42
    /**
43
     * Language currently in use
44
     * @var string
45
     */
46
    protected $currentLanguage;
47
48
    /**
49
     * Language currently in use (ID)
50
     * @var string
51
     */
52
    protected $currentLanguageId;
53
54
    /**
55
     * All the translation strings
56
     * @var array
57
     */
58
    protected $strings = [];
59
60
    /**
61
     * Context of the current page
62
     * @var string
63
     */
64
    protected $pageContext;
65
66
    /**
67
     * Strings cache
68
     * @var array
69
     */
70
    protected $stringsRaw = [];
71
72
    /**
73
     * @var CacheRepository The cache to store the terms in
74
     */
75
    protected $cache;
76
77
    /**
78
     * @var Session The session store
79
     */
80
    protected $session;
81
82
    /**
83 177
     * @var Log The log writer
84
     */
85 177
    protected $log;
86 177
87 177
    /**
88 177
     * @var ConfigRepository
89
     */
90 177
    protected $config;
91 177
92 177
    /**
93 177
     * @var Application
94 177
     */
95
    protected $app;
96 177
97
    /**
98 177
     * Prepare the translation service
99 177
     *
100 177
     * @param Application $app
101 177
     */
102 177
    public function __construct(Application $app, CacheRepository $cache, Session $session, Log $log, ConfigRepository $config)
103
    {
104 177
        $this->app = $app;
105
        $this->cache = $cache;
106 177
        $this->session = $session;
107 177
        $this->log = $log;
108
109
        $lang = $this->cache->remember(
110 177
            'Lang::List',
111 177
            60 * 24,
112 177
            function () {
113
                return Language::all();
114
            }
115
        );
116
117
        foreach ($lang as $l) {
118
            $this->languagesIso[$l->iso] = $this->languagesId[$l->id] = [
119
                'id' => $l->id,
120
                'name' => $l->title,
121
                'iso' => $l->iso,
122
            ];
123
        }
124
125
        $locale = $config['app.locale'];
126 177
        $fallback = $config['app.fallback_locale'];
127
128
        //current default language
129 177
        $language = $this->getDefaultLanguage($locale, $fallback);
130
        $this->setLanguage($language);
131 177
    }
132
133
    protected function getFilePath() {
134
        return $this->app['path.storage'] . '/languages/';
135
    }
136
137 177
    /**
138
     * Detects the default languages in the following order :
139
     *
140
     * 1. Is a user session var defined ?
141
     * 2. Can we take it from the browser ?
142 177
     * 3. Take the site default
143
     *
144
     * @param $locale string
145 177
     * @param $fallback string
146 177
     * @throws Exception if a default language cannot be found
147 177
     * @return string
148
     */
149 177
    public function getDefaultLanguage($locale, $fallback)
150
    {
151 177
        //1. detect user session
152
        $session_lang = $this->session->get('language');
153
154
        if (!empty($session_lang)) {
155
            return $session_lang;
156
        }
157
158
        //TODO :: move languages to subdomains
159
        //Special workaroud : only french for the moment
160
        if (defined('F_LANGUAGES') && !F_LANGUAGES) {
161
            return 'fr';
162
        }
163
164
        //2. detect browser language
165
        $browser_languages = Request::getLanguages();
166
167
        //is one of them available ?
168
        foreach ($browser_languages as $lang) {
169
            if ($this->isAvailable($lang)) {
170
                $this->session->put('language', $lang);
171
172
                return $lang;
173
            }
174
        }
175
176
        //3. Site default
177
        if ($this->isAvailable($locale)) {
178
            return $locale;
179
        }
180
181
        //4. Site fallback
182
        if ($this->isAvailable($fallback)) {
183
            return $fallback;
184
        }
185
186
        throw new \Exception('Cannot find an adapted language');
187
    }
188
189
    /**
190
     * Set the current language
191 177
     *
192
     * @param  string $language
193 177
     * @return bool
194
     */
195
    public function setCurrentLanguage($language)
196
    {
197 177
        if (!$this->isAvailable($language)) {
198
            return false;
199 177
        }
200
201
        $this->session->put('language', $language);
202 177
203
        $this->setLanguage($language);
204
205
        return true;
206 177
    }
207 177
208
    /**
209
     * Load a language file
210
     *
211
     * @param  string $language
212
     * @return bool
213 27
     */
214
    public function loadLanguage($language)
215 27
    {
216
        if ($this->isLoaded($language)) {
217
            return;
218
        }
219
220
        $langfile = $language . '.php';
221
222 150
        $this->strings[$language] = [];
223
224 150
        // Determine where the language file is and load it
225
        $filePath = $this->getFilePath();
226
        if (file_exists($filePath . $langfile)) {
227
            $this->strings[$language] = include $filePath . $langfile;
228
        }
229
230
        $this->languagesLoaded[] = $language;
231
    }
232
233 177
    /**
234
     * Get the current language
235 177
     * @return string
236 156
     */
237
    public function getCurrent()
238
    {
239 177
        return $this->currentLanguage;
240 177
    }
241 177
242
    /**
243
     * Get the current language id
244 177
     * @return int
245 21
     */
246 21
    public function getCurrentId()
247 177
    {
248 177
        return $this->currentLanguageId;
249 177
    }
250
251
    /**
252
     * Set the language to use
253
     *
254
     * @param  string $language
255 177
     * @return bool
256 177
     */
257 177
    public function setLanguage($language)
258
    {
259
        if ($language == $this->currentLanguage) {
260
            return;
261
        }
262
263
        if (!$this->isLoaded($language)) {
264
            $this->loadLanguage($language);
265 177
        }
266
267 177
        switch ($language) {
268
            case 'fr':
269
                setlocale(LC_ALL, 'fr_FR.utf8', 'fr_FR.UTF-8', 'fr_FR@euro', 'fr_FR', 'french');
270
                break;
271
            case 'en':
272
                setlocale(LC_ALL, 'en_US.utf8', 'en_US.UTF-8', 'en_US');
273
                break;
274
            case 'de':
275
                setlocale(LC_ALL, 'de_DE.utf8', 'de_DE.UTF-8', 'de_DE@euro', 'de_DE', 'deutsch');
276
                break;
277
        }
278
279
        $this->currentLanguage = $language;
280
        $this->currentLanguageId = $this->languagesIso[$language]['id'];
281
    }
282
283
    /**
284
     * Checks if a language is loaded or not
285
     *
286
     * @param  string $language
287
     * @return bool
288
     */
289
    protected function isLoaded($language)
290
    {
291 177
        return in_array($language, $this->languagesLoaded);
292
    }
293 177
294
    /**
295
     * Checks if a language is the default one
296
     *
297
     * @param  string $language
298
     * @return bool
299
     */
300
    protected function isDefault($language)
301
    {
302
        if ($language == 'default' or $this->currentLanguage == $language) {
303
            return true;
304
        }
305
306
        return false;
307
    }
308
309
    /**
310
     * Checks if the language is availavble
311
     *
312
     * @param  string $language
313
     * @return bool
314
     */
315
    protected function isAvailable($language)
316
    {
317
        return array_key_exists($language, $this->languagesIso);
318
    }
319
320
    /**
321 36
     * Retrieve languages.
322
     *
323 36
     * this is a hybrid method.
324 33
     *
325
     *
326
     *     I18N::languages();
327 3
     *     returns ['fr' => ['id' => 1, 'name' => 'francais', 'iso' => 'fr'], 'en' => ...]
328
     *
329
     *
330
     *     I18N::languages('fr');
331
     *     returns ['id' => 1, 'name' => 'francais', 'iso' => 'fr']
332
     *
333
     *
334
     *     I18N::languages(1);
335 3
     *     returns ['id' => 1, 'name' => 'francais', 'iso' => 'fr']
336
     *
337
     *
338
     *     I18N::languages('fr', 'id');
339 3
     *     returns 1
340
     *
341
     * @param int|string $key
342
     * @param string $subkey
343
     * @return array
344
     */
345
    public function languages($key = null, $subkey = null)
346
    {
347
        if ($key === null) {
348
            return $this->languagesIso;
349
        }
350
351
        if (is_int($key)) {
352
            if (is_null($subkey)) {
353
                return $this->languagesId[$key];
354
            }
355
356
            return $this->languagesId[$key][$subkey];
357
        }
358
359
        if (is_null($subkey)) {
360
            return $this->languagesIso[$key];
361
        }
362
363
        return $this->languagesIso[$key][$subkey];
364
    }
365
366
    public function languagesForSelect()
367
    {
368
        $languages = [];
369
        foreach (static::languages() as $lang) {
370
            $languages[$lang['id']] = t($lang['name'], [], 'languages');
371
        }
372
373
        return $languages;
374
    }
375
376
    protected function translateGetString($context, $language, $string_id)
377
    {
378
        $row = Translation::select('text')
379
            ->where('string_id', $string_id)
380
            ->where('language_id', $this->languagesIso[$language]['id'])
381
            ->first();
382
383
        if ($row !== null) {
384
            return $row->text;
385
        }
386
387
        return false;
388
    }
389
390
    protected function translateInsertString($context, $text)
391
    {
392
        $string = new StringModel();
393
        $string->date_creation = mysql_datetime();
394
        $string->context = $context;
395
        $string->string = $text;
396
        $string->save();
397
398
        //insertion de la traduction par défaut.
399
        $translation = new Translation();
400
        $translation->string_id = $string->id;
401
        $translation->language_id = $this->languagesIso[$this->config['app.locale']]['id'];
402
        $translation->date_edition = mysql_datetime();
403
        $translation->text = $text;
404
        $translation->save();
405
406
        return $translation;
407
    }
408
409
    /**
410
     * Retreive a string to translate
411
     *
412
     * if it doesn't find it, put it in the database
413
     *
414
     * @param  string $string
415
     * @param  string $context
416
     * @param  string $language
417
     * @return string
418
     */
419
    public function translate($string, $context = 'default', $language = 'default')
420
    {
421
        if ($this->isDefault($language)) {
422
            $language = $this->currentLanguage;
423
        } else {
424
            $this->setLanguage($language);
425
        }
426
427
        //get string from cache
428
        if (array_key_exists($context, $this->strings[$language]) &&
429
            array_key_exists($string, $this->strings[$language][$context])) {
430
            return $this->strings[$language][$context][$string];
431
        }
432
433
        //check in db
434
        $db_string = StringModel::select('id', 'date_creation')
435
            ->where('string', $string)
436
            ->where('context', $context)
437
            ->first();
438
439
        if (!$db_string) {
440
            $this->translateInsertString($context, $string);
441
442
            return $string;
443
        }
444
445
        $text = $this->translateGetString($context, $language, $db_string->id);
446
        if ($text) {
447
            $this->strings[$language][$context][$string] = $text;
448
449
            return $text;
450
        }
451
452
        return $string;
453
    }
454
455
    /**
456
     * Get the cached strings
457
     *
458
     * @return array
459
     */
460
    public function getRawStrings()
461
    {
462
        return $this->stringsRaw;
463
    }
464
465
    /**
466
     * Get the page's context
467
     *
468
     * @return string
469
     */
470
    public function getContext()
471
    {
472
        if ($this->pageContext) {
473
            return $this->pageContext;
474
        }
475
476
        $current = \Route::getCurrentRoute();
477
478
        if (!$current) {
479
            return 'default';
480
        }
481
482
        if ($current->getName()) {
483
            return $this->pageContext = $current->getName();
484
        }
485
486
        $action = $current->getAction();
487
        if (array_key_exists('controller', $action)) {
488
            return $this->pageContext = $action['controller'];
489
        }
490
491
        return $this->pageContext = implode('/', array_map(['Str', 'slug'], explode('/', $current->getUri())));
492
    }
493
494
    public function generate()
495
    {
496
        $filePath = $this->getFilePath();
497
498
        if (!is_dir($filePath)) {
499
            mkdir($filePath, 0755, true);
500
            chmod($filePath, 0755);
501
        }
502
503
        foreach ($this->languages() as $lang => $d) {
504
            $strings = StringModel::select('string', 'text', 'context')
505
                ->where('language_id', $d['id'])
506
                ->join((new Translation)->getTable(), 'strings.id', '=', 'string_id', 'left')
507
                ->get();
508
509
            $final_strings = [];
510
            foreach ($strings as $s) {
511
                $final_strings[$s->context][$s->string] = $s->text;
512
            }
513
514
            file_put_contents("{$filePath}{$lang}.php", '<?php return ' . var_export($final_strings, true) . ';');
515
        }
516
    }
517
}
518