I18N::languages()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 18.6936

Importance

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