Trans::_()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 19
rs 9.9332
cc 4
nc 8
nop 3
1
<?php
2
3
/* Copyright (C) 2024      Rafael San José      <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace Alxarafe\Lib;
20
21
use Alxarafe\Tools\Debug;
22
use Alxarafe\Tools\Dispatcher;
23
use Symfony\Component\Translation\Loader\ArrayLoader;
24
use Symfony\Component\Translation\Translator;
25
use Symfony\Component\Yaml\Yaml;
26
27
abstract class Trans
28
{
29
    /**
30
     * It is the default language when none has been specified yet.
31
     */
32
    public const FALLBACK_LANG = 'en';
33
34
    private static $translator;
35
36
    private static $translations = [];
37
38
    /**
39
     * List of strings without translation, for debugging bar purposes.
40
     *
41
     * @var array
42
     */
43
    private static $missingStrings = [];
44
45
    private static $missingStringsDebug;
46
47
    /**
48
     * Gets a list of available languages
49
     *
50
     * @return array
51
     */
52
    public static function getAvailableLanguages()
53
    {
54
        $routes = [
55
            '/Lang',
56
            '/vendor/rsanjoseo/alxarafe/src/Lang'
57
        ];
58
        $result = [];
59
        foreach (Functions::getFirstNonEmptyDirectory($routes, '.yaml') as $lang) {
60
            $result[$lang] = self::_($lang);
61
        }
62
        return $result;
63
    }
64
65
    /**
66
     * Return a text in the selected language.
67
     *
68
     * @param string $message
69
     * @param array $parameters
70
     * @param string|null $locale
71
     * @return string
72
     */
73
    public static function _(string $message, array $parameters = [], ?string $locale = null): string
74
    {
75
        if (!isset($locale)) {
76
            $locale = self::$translator->getLocale();
77
        }
78
79
        $params = [];
80
        foreach ($parameters as $name => $value) {
81
            $params['%' . $name . '%'] = $value;
82
        }
83
84
        if (!isset(self::$translations[$message])) {
85
            self::$missingStrings[$message] = [
86
                'lang' => self::FALLBACK_LANG,
87
                'text' => 'Not defined!',
88
            ];
89
        }
90
91
        return self::$translator->trans($message, $params, null, $locale);
92
    }
93
94
    /**
95
     * Return a text in the selected language.
96
     * Call directly to extra short function _
97
     *
98
     * @param $message
99
     * @param array $parameters
100
     * @param $locale
101
     * @return mixed
102
     */
103
    public static function trans($message, array $parameters = [], $locale = null)
104
    {
105
        return self::_($message, $parameters, $locale);
106
    }
107
108
    /**
109
     * Initializes the translator.
110
     *
111
     * @return bool
112
     */
113
    public static function initialize()
114
    {
115
        if (isset(self::$translator)) {
116
            return true;
117
        }
118
119
        self::$translator = new Translator(self::FALLBACK_LANG);
120
        return isset(self::$translator);
121
    }
122
123
    /**
124
     * Returns an array with the strings that are not found in your language.
125
     * Keep in mind that those that do exist in your main language also appear
126
     * (e.g. it may be in "en", but not in "en_US").
127
     * Generally, for extended languages, such as "en_US", only texts that
128
     * change from "en" should be included.
129
     *
130
     * @return array
131
     */
132
    public static function getMissingStrings(): array
133
    {
134
        self::$missingStringsDebug = [];
135
        $locale = self::$translator->getLocale();
136
        foreach (self::$missingStrings as $name => $value) {
137
            if ($value['lang'] === $locale) {
138
                continue;
139
            }
140
            self::$missingStringsDebug[$name] = '(' . $value['lang'] . ') ' . $value['text'];
141
        }
142
        return self::$missingStringsDebug;
143
    }
144
145
    /**
146
     * Loads core and module lang files
147
     *
148
     * @param $lang
149
     * @return void
150
     */
151
    public static function setLang($lang)
152
    {
153
        self::$translations = [];
154
        self::$translator->setLocale($lang);
155
        self::getTranslations($lang);
156
    }
157
158
    private static function getTranslations($lang)
159
    {
160
        $app_route = constant('BASE_PATH') . '/..';
161
        $main_route = constant('BASE_PATH') . '/../vendor/rsanjoseo/alxarafe/src';
162
163
        $routes = [];
164
        $routes[] = $app_route;
165
        $routes[] = $main_route;
166
        if (isset($_GET[Dispatcher::MODULE])) {
167
            $module = $_GET[Dispatcher::MODULE];
168
            $routes[] = $app_route . '/Modules/' . $module;
169
            $routes[] = $main_route . '/Modules/' . $module;
170
        }
171
172
        self::$translator->addLoader('array', new ArrayLoader());
173
        foreach ($routes as $route) {
174
            $route .= '/Lang';
175
            $route = realpath($route);
176
            if ($route === false || !file_exists($route)) {
177
                continue;
178
            }
179
            if ($lang !== self::FALLBACK_LANG) {
180
                self::loadLang(self::FALLBACK_LANG, $route);
181
            }
182
            self::loadLang($lang, $route);
183
        }
184
185
        self::$translator->addResource('array', self::$translations, $lang);
186
    }
187
188
    /**
189
     * Loads language files for the specified path and language.
190
     *
191
     * @param string $lang
192
     * @param string $folder
193
     */
194
    private static function loadLang(string $lang, string $folder): void
195
    {
196
        if (strlen($lang) > 2) {
197
            self::loadLang(substr($lang, 0, 2), $folder);
198
        }
199
200
        $filename = $folder . '/' . $lang . '.yaml';
201
        if (!file_exists($filename)) {
202
            Debug::message($filename . ' not found');
203
            return;
204
        }
205
206
        $translates = Yaml::parseFile($filename);
207
        if (!is_array($translates)) {
208
            Debug::message($filename . ' is not a valid YAML file');
209
            return;
210
        }
211
212
        foreach ($translates as $name => $translate) {
213
            self::$translations[$name] = $translate;
214
            self::$missingStrings[$name] = [
215
                'lang' => $lang,
216
                'text' => $translate
217
            ];
218
        }
219
    }
220
}
221