Passed
Push — master ( fd31ea...8a1c29 )
by Fran
09:11
created

extractBaseTranslations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 10
ccs 7
cts 7
cp 1
crap 2
rs 10
1
<?php
2
3
namespace PSFS\base\extension;
4
5
use PSFS\base\config\Config;
6
use PSFS\base\Logger;
7
use PSFS\base\Security;
8
use PSFS\base\types\helpers\I18nHelper;
9
use PSFS\base\types\helpers\Inspector;
10
use PSFS\base\types\traits\SingletonTrait;
11
use Twig\Extension\AbstractExtension;
12
use Twig\TokenParser\BlockTokenParser;
13
use Twig\TwigFilter;
14
15
/**
16
 * Class CustomTranslateExtension
17
 * @package PSFS\base\extension
18
 */
19
class CustomTranslateExtension extends AbstractExtension
20
{
21
    use SingletonTrait;
22
23
    const CUSTOM_LOCALE_SESSION_KEY = '__PSFS_CUSTOM_LOCALE_KEY__';
24
    const LOCALE_CACHED_VERSION = '__PSFS_LOCALE_VERSION__';
25
    const LOCALE_CACHED_TAG = '__PSFS_TRANSLATIONS__';
26
27
    /**
28
     * @var array
29
     */
30
    protected static $translations = [];
31
32
    /**
33
     * @var array
34
     */
35
    protected static $translationsKeys = [];
36
    /**
37
     * @var string
38
     */
39
    protected static $locale = 'es_ES';
40
    /**
41
     * @var bool
42
     */
43
    protected static $generate = false;
44
    /**
45
     * @var string
46
     */
47
    protected static $filename = '';
48
49
    /**
50
     * @return array|mixed
51
     */
52 2
    protected static function extractBaseTranslations($locale = null)
53
    {
54 2
        $locale = $locale ?? self::$locale;
55
        // Gather always the base translations
56 2
        $standardTranslations = [];
57 2
        self::$filename = implode(DIRECTORY_SEPARATOR, [LOCALE_DIR, 'custom', $locale . '.json']);
58 2
        if (file_exists(self::$filename)) {
59 2
            $standardTranslations = json_decode(file_get_contents(self::$filename), true);
60
        }
61 2
        return $standardTranslations;
62
    }
63
64
    /**
65
     * @param string $locale
66
     * @return void
67
     */
68 2
    protected static function generateTranslationsKeys($locale) {
69 2
        self::$translationsKeys[$locale] = [];
70 2
        foreach(self::$translations[$locale] as $key => $value) {
71 2
            $tKey = mb_convert_case($key, MB_CASE_LOWER, "UTF-8");
72 2
            self::$translationsKeys[$locale][$tKey] = $key;
73
        }
74
    }
75
76
    /**
77
     * @param string $customKey
78
     * @param bool $forceReload
79
     * @param bool $useBase
80
     */
81 2
    protected static function translationsCheckLoad($customKey = null, $forceReload = false, $useBase = false)
82
    {
83 2
        Inspector::stats('[translationsCheckLoad] Start checking translations load', Inspector::SCOPE_DEBUG);
84 2
        $session = Security::getInstance();
85 2
        $session_locale = $session->getSessionKey(I18nHelper::PSFS_SESSION_LOCALE_KEY) ?? $session->getSessionKey(I18nHelper::PSFS_SESSION_LANGUAGE_KEY);
86 2
        self::$locale = $forceReload ? $session_locale : I18nHelper::extractLocale($session_locale);
87 2
        $locale = self::$locale;
88 2
        $version = $session->getSessionKey(self::LOCALE_CACHED_VERSION);
89 2
        $configVersion = self::$locale . '_' . Config::getParam('cache.var', 'v1');
90 2
        if ($forceReload) {
91 1
            Inspector::stats('[translationsCheckLoad] Force translations reload', Inspector::SCOPE_DEBUG);
92 1
            self::dropInstance();
93 1
            $version = null;
94 1
            self::$translations[self::$locale] = [];
95
        }
96 2
        if((!array_key_exists($locale, self::$translations) || count(self::$translations[$locale]) === 0) && strlen($locale) === 2) {
97
            $locale = $locale . '_' . strtoupper($locale);
98
            if(array_key_exists($locale, self::$translations)) {
99
                self::$translations[self::$locale] = self::$translations[$locale];
100
                self::generateTranslationsKeys(self::$locale);
101
            }
102
        }
103 2
        if(!array_key_exists($locale, self::$translations) || count(self::$translations[$locale]) === 0) {
104 2
            Inspector::stats('[translationsCheckLoad] Extracting translations', Inspector::SCOPE_DEBUG);
105 2
            self::$generate = (boolean)Config::getParam('i18n.autogenerate', false);
106 2
            if(null !== $version && $version === $configVersion) {
107
                Inspector::stats('[translationsCheckLoad] Translations loaded from session', Inspector::SCOPE_DEBUG);
108
                self::$translations = $session->getSessionKey(self::LOCALE_CACHED_TAG);
109
            } else {
110 2
                if (!$useBase) {
111 2
                    $customKey = $customKey ?: $session->getSessionKey(self::CUSTOM_LOCALE_SESSION_KEY);
112
                }
113 2
                $standardTranslations = self::extractBaseTranslations();
114
                // If the project has custom translations, gather them
115 2
                if (null !== $customKey) {
116
                    Logger::log('[' . self::class . '] Custom key detected: ' . $customKey, LOG_INFO);
117
                    self::$filename = implode(DIRECTORY_SEPARATOR, [LOCALE_DIR, 'custom', $customKey, $locale . '.json']);
118 2
                } elseif (!empty($standardTranslations)) {
119 2
                    self::$translations[$locale] = $standardTranslations;
120 2
                    self::generateTranslationsKeys($locale);
121
                }
122
                // Finally we merge base and custom translations to complete all the i18n set
123 2
                if (file_exists(self::$filename)) {
124 2
                    Logger::log('[' . self::class . '] Custom locale detected: ' . $customKey . ' [' . $locale . ']', LOG_INFO);
125 2
                    self::$translations[$locale] = array_merge($standardTranslations, json_decode(file_get_contents(self::$filename), true));
126 2
                    self::generateTranslationsKeys($locale);
127 2
                    $session->setSessionKey(self::LOCALE_CACHED_TAG, self::$translations);
128 2
                    $session->setSessionKey(self::LOCALE_CACHED_VERSION, $configVersion);
129
                } elseif (null !== $customKey) {
130
                    self::translationsCheckLoad(null, $forceReload, true);
131
                }
132
            }
133
        }
134 2
        Inspector::stats('[translationsCheckLoad] Translations loaded', Inspector::SCOPE_DEBUG);
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 1
    public function getTokenParsers()
141
    {
142 1
        return [new BlockTokenParser()];
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 2
    public function getFilters()
149
    {
150 1
        return array(
151 1
            new TwigFilter('trans', function ($message) {
152 2
                return self::_($message);
153 1
            }),
154 1
        );
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function getName()
161
    {
162
        return 'PSFSi18n';
163
    }
164
165
    /**
166
     * @param $message
167
     * @param string $customKey
168
     * @param bool $forceReload
169
     * @return mixed|string
170
     */
171 12
    public static function _($message, $customKey = null, $forceReload = false)
172
    {
173 12
        if(0 === count(self::$translations) || $forceReload) {
174 2
            self::translationsCheckLoad($customKey, $forceReload);
175
        }
176
        // Set default translation to catch missing strings
177 12
        $isDebugMode = (bool)Config::getParam('debug', false);
178 12
        $translation = (bool)Config::getParam('debug', false) ? 'MISSING_TRANSLATION - ' . self::$locale : $message;
179
        // Check if the message is already translated ignoring the string case
180 12
        $key = mb_convert_case($message, MB_CASE_LOWER, "UTF-8");
181 12
        if(array_key_exists(self::$locale, self::$translationsKeys) && array_key_exists($key, self::$translationsKeys[self::$locale])) {
182 9
            $message = self::$translationsKeys[self::$locale][$key];
183
        }
184
        // Check if exists
185 12
        if (array_key_exists(self::$locale, self::$translations) && array_key_exists($message, self::$translations[self::$locale])) {
186 9
            $translation = self::$translations[self::$locale][$message];
187 4
        } else if(!$forceReload && !$isDebugMode) {
188
            $translation = gettext($message);
189
        }
190 12
        if (self::$generate) {
191
            self::generate($message, $translation);
192
        }
193 12
        return $translation;
194
    }
195
196
    /**
197
     * @param string $message
198
     * @param string $translation
199
     */
200
    protected static function generate($message, $translation)
201
    {
202
        if(!array_key_exists(self::$locale, self::$translations)) {
203
            self::$translations[self::$locale] = [];
204
            self::$translationsKeys[self::$locale] = [];
205
        }
206
        if (!array_key_exists($message, self::$translations)) {
207
            self::$translations[self::$locale][$message] = $translation;
208
            self::$translationsKeys[self::$locale][mb_convert_case($message, MB_CASE_LOWER, "UTF-8")] = $message;
209
        }
210
        file_put_contents(self::$filename, json_encode(array_unique(self::$translations), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
211
    }
212
}
213