Issues (7)

src/Translator.php (2 issues)

Severity
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
        $this->config = Config::get($this->configName);
45
46
        $this->setLocale($locale);
47
    }
48
49
    /**
50
     * Return the config value for given key
51
     *
52
     * @param string $key Key for the config value to get
53
     * @return string|array Config value for $key
54
     */
55
    public function getConfigValue($key) {
56
        return $this->config[$key];
57
    }
58
59
    /**
60
     * Actual translate function
61
     * $parameter and $locale are optional
62
     *
63
     * @param string $identifier The identifier of the translation
64
     * @param array|null $parameters The parameters to inject into the translation
65
     * @param string $locale The locale to which to translate to overrides the class location for one translation
66
     * @throws \Exception
67
     * @return string Returns the translation with replaced parameters
68
     *
69
     */
70
    public function translate($identifier, $parameters = null, $locale = null) {
71
        if ($locale !== null) {
72
            $locale = $this->validateLocale($locale);
73
        } else {
74
            $locale = $this->locale;
75
        }
76
77
        if (!isset($this->aHandler[$locale])) {
78
            $this->aHandler[$locale] = $this->createHandler($locale);
79
        }
80
81
        try {
82
            $translation = $this->aHandler[$locale]->getTranslation($identifier, $this->group);
83
        } catch (NotFoundResourceException $exception) {
84
            // Thrown when the Identifier wasn't found
85
            $this->log($exception, 'error');
86
87
            if ($this->config['listening_enabled'] === true) {
88
                $this->addMissingIdentifierToDB($identifier, $parameters, 'default');
89
            }
90
91
            $translation = $this->returnMissingTranslation($identifier, $locale);
92
93
        } catch (TranslationNotFoundException $exception) {
94
            // Thrown when no translation for the locale was found
95
            $this->log($exception, 'error');
96
97
            $translation = $this->returnMissingTranslation($identifier, $locale);
98
        }
99
100
        if (is_array($parameters)) {
101
            $translation = $this->replaceParameter($translation, $parameters);
102
        }
103
104
        if (session('translation_live_mode')) {
105
            $id = $this->aHandler[$locale]->getDatabaseID($identifier);
106
            $translation = $this->addLiveModeLink($translation, $id);
107
        }
108
109
        // Return the translation
110
        return $translation;
111
    }
112
113
    /**
114
     * Inject a Link to the edit page into the translation
115
     *
116
     * @param string $translation
117
     * @param integer $id
118
     * @return string
119
     */
120
    public function addLiveModeLink($translation, $id) {
121
122
        $route = route('translator.admin.edit', ['id' => $id]);
123
124
        $inject = "<translation-anchor onclick='window.open(\"$route\", \"_blank\")' style='position: absolute; z-index: 999; cursor: pointer;'>&#9875;</translation-anchor>";
125
126
        $translation = "$translation $inject";
127
128
        return $translation;
129
    }
130
131
    /**
132
     * Sets the Handler
133
     *
134
     * @param $locale
135
     * @return DefaultHandler
136
     */
137
    protected function createHandler($locale) {
138
        $handler_class = $this->config['handler'];
139
140
        if (session('translation_live_mode')) {
141
            $handler_class = DatabaseHandler::class;
142
        }
143
144
        try {
145
            $oHandler = new $handler_class($locale);
146
147
        } catch (\Exception $exception) {
148
149
            $this->log($exception, 'error');
150
            $this->log('Falling back to DefaultHandler', 'warning');
151
152
            $oHandler = new DefaultHandler($locale);
153
        }
154
155
        return $oHandler;
156
    }
157
158
    /**
159
     * Set the locale to use in translations
160
     *
161
     * @param string $locale The locale to use
162
     */
163
    public function setLocale($locale) {
164
        $locale = $this->validateLocale($locale);
165
166
        if (!isset($this->aHandler[$locale])) {
167
            $this->aHandler[$locale] = $this->createHandler($locale);
168
        }
169
170
        $this->locale = $locale;
171
    }
172
173
    /**
174
     * Add the missing identifier to the texts table in the database
175
     *
176
     * @param string $identifier The identifier to add to the db
177
     * @param array $parameters The parameters available for the translation
178
     * @param string $group The group to put the identifier in
179
     */
180
    public function addMissingIdentifierToDB($identifier, $parameters, $group) {
181
        if (!$this->hasIdentifier($identifier)) {
182
183
            $keys = [];
184
            if (is_array($parameters)) {
0 ignored issues
show
The condition is_array($parameters) is always true.
Loading history...
185
                foreach ($parameters as $key => $value) {
186
                    $keys[] = $key;
187
                }
188
            }
189
190
            TranslationIdentifier::create(
191
                [
192
                    "identifier"    => $identifier,
193
                    "parameters"    => $keys,
194
                    "group"         => isset($group) ? $group : 'default',
195
                    "page_name"     => app()->runningInConsole() ? '' : substr(request()->getRequestUri(), 1),
0 ignored issues
show
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

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