Test Setup Failed
Push — master ( 5d4742...dadafa )
by Avtandil
11:52 queued 14s
created

MultiLang::replaceMarkers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 8
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Longman\LaravelMultiLang;
6
7
use Closure;
8
use Illuminate\Cache\CacheManager as Cache;
9
use Illuminate\Database\DatabaseManager as Database;
10
use Illuminate\Http\Request;
11
use InvalidArgumentException;
12
use Symfony\Component\Translation\Loader\ArrayLoader;
13
use Symfony\Component\Translation\Translator;
14
15
class MultiLang
16
{
17
    /**
18
     * Language/Locale.
19
     *
20
     * @var string
21
     */
22
    protected $lang;
23
24
    /**
25
     * System environment
26
     *
27
     * @var string
28
     */
29
    protected $environment;
30
31
    /**
32
     * Config.
33
     *
34
     * @var \Longman\LaravelMultiLang\Config
35
     */
36
    protected $config;
37
38
    /**
39
     * Repository
40
     *
41
     * @var \Longman\LaravelMultiLang\Repository
42
     */
43
    protected $repository;
44
45
    /**
46
     * Texts.
47
     *
48
     * @var array
49
     */
50
    protected $texts;
51
52
    /**
53
     * Missing texts.
54
     *
55
     * @var array
56
     */
57
    protected $new_texts;
58
59
    /**
60
     * Application scope.
61
     *
62
     * @var string
63
     */
64
    protected $scope = 'global';
65
66
    /**
67
     * Translator instance
68
     *
69
     * @var \Symfony\Component\Translation\Translator
70
     */
71
    protected $translator;
72
73
    /**
74
     * Create a new MultiLang instance.
75
     *
76
     * @param string $environment
77
     * @param array $config
78
     * @param \Illuminate\Cache\CacheManager $cache
79
     * @param \Illuminate\Database\DatabaseManager $db
80 33
     */
81
    public function __construct(string $environment, array $config, Cache $cache, Database $db)
82 33
    {
83
        $this->environment = $environment;
84 33
85
        $this->setConfig($config);
86 33
87 33
        $this->setRepository(new Repository($this->config, $cache, $db));
88
    }
89
90
    /**
91
     * Set multilang config
92
     *
93
     * @param array $config
94
     * @return $this
95 33
     */
96
    public function setConfig(array $config): MultiLang
97 33
    {
98
        $this->config = new Config($config);
99 33
100
        return $this;
101
    }
102
103
    /**
104
     * Get multilang config
105
     *
106
     * @return \Longman\LaravelMultiLang\Config
107 1
     */
108
    public function getConfig(): Config
109
    {
110 1
111
        return $this->config;
112
    }
113
114
    /**
115
     * Set repository object
116
     *
117
     * @param \Longman\LaravelMultiLang\Repository $repository
118
     * @return $this
119 33
     */
120
    public function setRepository(Repository $repository): MultiLang
121 33
    {
122
        $this->repository = $repository;
123 33
124
        return $this;
125
    }
126
127
    /**
128
     * Get repository object
129
     *
130
     * @return \Longman\LaravelMultiLang\Repository
131 3
     */
132
    public function getRepository(): Repository
133 3
    {
134
        return $this->repository;
135
    }
136
137
    /**
138
     * Set application scope
139
     *
140
     * @param $scope
141
     * @return $this
142 1
     */
143
    public function setScope($scope): MultiLang
144 1
    {
145
        $this->scope = $scope;
146 1
147
        return $this;
148
    }
149
150
    /**
151
     * Get application scope
152
     *
153
     * @return string
154 1
     */
155
    public function getScope(): string
156 1
    {
157
        return $this->scope;
158
    }
159
160
    /**
161
     * Set locale
162
     *
163
     * @param  string $lang
164
     * @return void
165
     */
166 27
    public function setLocale(string $lang)
167
    {
168 27
        if (! $lang) {
169 1
            throw new InvalidArgumentException('Locale is empty');
170
        }
171 26
        $this->lang = $lang;
172
    }
173 26
174 23
    public function loadTexts(?string $locale = null, ?string $scope = null): array
175
    {
176
        if (is_null($locale)) {
177 26
            $locale = $this->getLocale();
178 26
        }
179
180
        if (is_null($scope)) {
181
            $scope = $this->getScope();
182
        }
183
184
        if ($this->environment !== 'production' || $this->config->get('cache.enabled', true) === false) {
185
            $texts = $this->repository->loadFromDatabase($locale, $scope);
186
        } else {
187 23
            if ($this->repository->existsInCache($locale, $scope)) {
188
                $texts = $this->repository->loadFromCache($locale, $scope);
189 23
            } else {
190 20
                $texts = $this->repository->loadFromDatabase($locale, $scope);
191
                $this->repository->storeInCache($locale, $texts, $scope);
192 20
            }
193
        }
194
195 4
        $this->createTranslator($locale, $scope, $texts);
196 1
197
        $this->texts = $texts;
198 4
199 4
        return $texts;
200
    }
201
202 4
    protected function createTranslator(string $locale, string $scope, array $texts): Translator
203
    {
204
        $this->translator = new Translator($locale);
205
        $this->translator->addLoader('array', new ArrayLoader());
206
        $this->translator->addResource('array', $texts, $locale, $scope);
207
208
        return $this->translator;
209
    }
210
211
    /**
212 9
     * Get translated text
213
     *
214
     * @param  string $key
215 9
     * @param  array $replacements
216 1
     * @return string
217
     */
218
    public function get(string $key, array $replacements = []): string
219 8
    {
220 1
        if (! $this->getConfig()->get('use_texts', true)) {
221
            throw new InvalidArgumentException('Using texts from database is disabled in config');
222
        }
223 7
224 4
        if (empty($key)) {
225
            throw new InvalidArgumentException('Text key not provided');
226 4
        }
227
228
        if (! $this->lang) {
229 3
            return $key;
230
        }
231 3
232
        if (is_null($this->texts)) {
233
            // Load texts from storage
234
            $this->loadTexts();
235
        }
236
237
        if (! isset($this->texts[$key])) {
238
            $this->queueToSave($key);
239
        }
240
241 7
        if (! empty($replacements)) {
242
            $keys = array_keys($replacements);
243 7
            $keys = array_map(static function ($v) {
244 6
                return ':' . $v;
245
            }, $keys);
246
            $replacements = array_combine($keys, $replacements);
247 1
        }
248
249
        return $this->translator->trans($key, $replacements, $this->getScope());
250
    }
251
252
    /**
253
     * Get redirect url in middleware
254
     *
255
     * @param \Illuminate\Http\Request $request
256
     * @return string
257 1
     */
258
    public function getRedirectUrl(Request $request): string
259 1
    {
260
        $exclude_patterns = $this->config->get('exclude_segments', []);
261 1
        if (! empty($exclude_patterns)) {
262 1
            if (call_user_func_array([$request, 'is'], $exclude_patterns)) {
263 1
                return '';
264 1
            }
265 1
        }
266
267
        $locale = $request->segment(1);
268
        $fallback_locale = $this->config->get('default_locale', 'en');
269 1
        if (! empty($locale) && strlen($locale) === 2) {
270
            $locales = $this->config->get('locales', []);
271
272
            if (! isset($locales[$locale])) {
273
                $segments = $request->segments();
274
                $segments[0] = $fallback_locale;
275
                $url = implode('/', $segments);
276
                if ($query_string = $request->server->get('QUERY_STRING')) {
277
                    $url .= '?' . $query_string;
278
                }
279
280 1
                return $url;
281 1
            }
282 1
        } else {
283
            $segments = $request->segments();
284
            $url = $fallback_locale . '/' . implode('/', $segments);
285
            if ($query_string = $request->server->get('QUERY_STRING')) {
286
                $url .= '?' . $query_string;
287
            }
288
289
            return $url;
290
        }
291 5
292
        return '';
293 5
    }
294 5
295 5
    /**
296 5
     * Detect locale based on url segment
297
     *
298
     * @param \Illuminate\Http\Request $request
299
     * @return string
300 5
     */
301 4
    public function detectLocale(Request $request): string
302
    {
303 4
        $locale = $request->segment(1);
304 3
        $locales = $this->config->get('locales');
305 3
306 3
        if (isset($locales[$locale])) {
307 3
            return $locales[$locale]['locale'] ?? $locale;
308 1
        }
309
310
        return (string) $this->config->get('default_locale', 'en');
311 4
    }
312
313
    /**
314 1
     * Wrap routes to available languages group
315 1
     *
316 1
     * @param \Closure $callback
317
     * @return void
318
     */
319
    public function routeGroup(Closure $callback)
320 1
    {
321
        $router = app('router');
322
323 1
        $locales = $this->config->get('locales', []);
324
325
        foreach ($locales as $locale => $val) {
326
            $router->group([
327
                'prefix' => $locale,
328
                'as'     => $locale . '.',
329
            ], $callback);
330
        }
331
    }
332 2
333
    /**
334 2
     * Get texts
335 2
     *
336
     * @return array
337 2
     */
338 1
    public function getTexts(): array
339
    {
340
341 1
        return $this->texts;
342
    }
343
344
    /**
345
     * Get all texts
346
     *
347
     * @param string $lang
348
     * @param string $scope
349
     * @return array
350
     */
351
    public function getAllTexts(?string $lang = null, ?string $scope = null): array
352
    {
353
        return $this->repository->loadAllFromDatabase($lang, $scope);
354
    }
355
356
    /**
357
     * Set texts manually
358
     *
359
     * @param  array $texts_array
360
     * @return \Longman\LaravelMultiLang\MultiLang
361
     */
362
    public function setTexts(array $texts_array): MultiLang
363
    {
364
        $texts = [];
365
        foreach ($texts_array as $key => $value) {
366
            $texts[$key] = $value;
367
        }
368
369
        $this->texts = $texts;
370
371
        $this->createTranslator($this->getLocale(), $this->getScope(), $texts);
372
373
        return $this;
374
    }
375
376
    /**
377
     * Queue missing texts
378
     *
379
     * @param  string $key
380
     * @return void
381
     */
382
    protected function queueToSave(string $key)
383
    {
384
        $this->new_texts[$key] = $key;
385
    }
386
387
    /**
388
     * Get language prefixed url
389
     *
390 4
     * @param string $path
391
     * @param string $lang
392
     * @return string
393 4
     */
394
    public function getUrl(string $path, ?string $lang = null): string
395
    {
396
        $locale = $lang ?: $this->getLocale();
397
        if ($locale) {
398
            $path = $locale . '/' . $this->removeLocaleFromPath($path);
399
        }
400
401
        return $path;
402
    }
403 1
404
    /**
405 1
     * Remove locale from the path
406
     *
407
     * @param string $path
408
     * @return string
409
     */
410
    private function removeLocaleFromPath(string $path): string
411
    {
412
        $lang_path = $path;
413
414 4
        // Remove domain from path
415
        $app_url = config('app.url', '');
416 4
        if (! empty($app_url) && mb_substr($lang_path, 0, mb_strlen($app_url)) === $app_url) {
417 4
            $lang_path = ltrim(str_replace($app_url, '', $lang_path), '/');
418 4
        }
419
420
        $locales = $this->config->get('locales');
421 4
        $locale = mb_substr($lang_path, 0, 2);
422
        if (isset($locales[$locale])) {
423 4
            return mb_substr($lang_path, 3);
424
        }
425
426
        return $path;
427
    }
428
429
    /**
430
     * Get language prefixed route
431
     *
432 4
     * @param string $name
433
     * @return string
434 4
     */
435 4
    public function getRoute(string $name): string
436
    {
437
        $locale = $this->getLocale();
438
        if ($locale) {
439
            $name = $locale . '.' . $name;
440
        }
441
442
        return $name;
443
    }
444 4
445
    /**
446 4
     * Check if autosave allowed
447 4
     *
448 4
     * @return bool
449
     */
450
    public function autoSaveIsAllowed()
451 4
    {
452
        if ($this->environment === 'local' && $this->config->get('db.autosave', true)) {
453
            return true;
454
        }
455
456
        return false;
457
    }
458
459
    /**
460 4
     * Get locale
461
     *
462 4
     * @return string
463 4
     */
464 4
    public function getLocale()
465 1
    {
466
        return $this->lang;
467
    }
468 4
469
    /**
470
     * Get available locales
471
     *
472
     * @return array
473
     */
474
    public function getLocales(): array
475
    {
476
        return (array) $this->config->get('locales');
477 2
    }
478
479 2
    /**
480 2
     * Save missing texts
481 1
     *
482
     * @return bool
483
     */
484 2
    public function saveTexts(): bool
485
    {
486
        if (empty($this->new_texts)) {
487
            return false;
488
        }
489
490
        return $this->repository->save($this->new_texts, $this->scope);
491
    }
492
}
493