-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTranslate.php
More file actions
268 lines (217 loc) · 7.71 KB
/
Translate.php
File metadata and controls
268 lines (217 loc) · 7.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
<?php
/**
* This file is part of Blitz PHP framework.
*
* (c) 2022 Dimitri Sitchet Tomkeu <devcode.dst@gmail.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace BlitzPHP\Translator;
use BlitzPHP\Autoloader\Autoloader;
use BlitzPHP\Autoloader\Locator;
use BlitzPHP\Contracts\Autoloader\LocatorInterface;
use InvalidArgumentException;
use MessageFormatter;
/**
* Gérer les messages système et la localisation.
*
* Basé sur les paramètres régionaux, construit sur l'internationalisation de PHP.
*
* @credit <a href="http://codeigniter.com">CodeIgniter4 - CodeIgniter\Language\Language</a>
*/
class Translate
{
/**
* Stocke les lignes de langue récupérées à partir des fichiers pour une récupération
* plus rapide lors d'une deuxième utilisation.
*/
protected array $language = [];
/**
* Valeur booléenne indiquant si les bibliothèques intl existent sur le système.
*/
protected bool $intlSupport = false;
/**
* Stocke les noms de fichiers qui ont été chargés afin que nous ne les chargions plus.
*/
protected array $loadedFiles = [];
/**
* Constructor
*
* @param string $locale La langue/paramètres régionaux actuels avec lesquels travailler.
*/
public function __construct(protected string $locale, protected ?LocatorInterface $locator = null)
{
if (class_exists(MessageFormatter::class)) {
$this->intlSupport = true;
}
}
/**
* Définit les paramètres régionaux actuels à utiliser lors de l'exécution de recherches de chaînes.
*/
public function setLocale(?string $locale = null): self
{
if ($locale !== null) {
$this->locale = $locale;
}
return $this;
}
public function getLocale(): string
{
return $this->locale;
}
/**
* Analyse la chaîne de langue d'un fichier, charge le fichier, si nécessaire, en obtenant la ligne.
*
* @return list<string>|string
*/
public function getLine(string $line, array $args = [])
{
// si aucun fichier n'est donné, il suffit d'analyser la ligne
if (! str_contains($line, '.')) {
return $this->formatMessage($line, $args);
}
// Analysez le nom du fichier et l'alias réel. Chargera le fichier de langue et les chaînes.
[$file, $parsedLine] = $this->parseLine($line, $this->locale);
$output = $this->getTranslationOutput($this->locale, $file, $parsedLine);
if ($output === null && strpos($this->locale, '-')) {
[$locale] = explode('-', $this->locale, 2);
[$file, $parsedLine] = $this->parseLine($line, $locale);
$output = $this->getTranslationOutput($locale, $file, $parsedLine);
}
// si toujours introuvable, essayez l'anglais
if ($output === null) {
[$file, $parsedLine] = $this->parseLine($line, 'en');
$output = $this->getTranslationOutput('en', $file, $parsedLine);
}
$output ??= $line;
return $this->formatMessage($output, $args);
}
/**
* @return array|string|null
*/
protected function getTranslationOutput(string $locale, string $file, string $parsedLine)
{
$output = $this->language[$locale][$file][$parsedLine] ?? null;
if ($output !== null) {
return $output;
}
foreach (explode('.', $parsedLine) as $row) {
if (! isset($current)) {
$current = $this->language[$locale][$file] ?? null;
}
$output = $current[$row] ?? null;
if (is_array($output)) {
$current = $output;
}
}
if ($output !== null) {
return $output;
}
$row = current(explode('.', $parsedLine));
$key = substr($parsedLine, strlen($row) + 1);
return $this->language[$locale][$file][$row][$key] ?? null;
}
/**
* Analyse la chaîne de langue qui doit inclure le nom de fichier
* comme premier segment (séparé par un point).
*/
protected function parseLine(string $line, string $locale): array
{
$file = substr($line, 0, strpos($line, '.'));
$line = substr($line, strlen($file) + 1);
if (! isset($this->language[$locale][$file]) || ! array_key_exists($line, $this->language[$locale][$file])) {
$this->load($file, $locale);
}
return [$file, $line];
}
/**
* Formatage avancé des messages.
*
* @param array|string $message
* @param list<string> $args
*
* @return array|string
*/
protected function formatMessage($message, array $args = [])
{
if (! $this->intlSupport || $args === []) {
return $message;
}
if (is_array($message)) {
foreach ($message as $index => $value) {
$message[$index] = $this->formatMessage($value, $args);
}
return $message;
}
$formatted = MessageFormatter::formatMessage($this->locale, $message, $args);
if ($formatted === false) {
throw new InvalidArgumentException(sprintf("Format de message non valide\u{a0}:\u{a0}%s, arguments\u{a0}:\u{a0}%s", $message, implode(', ', $args)));
}
return $formatted;
}
/**
* Charge un fichier de langue dans les paramètres régionaux actuels.
* Si $return est vrai, renverra le contenu du fichier,
* sinon fusionnera avec les lignes de langage existantes.
*
* @return array|void
*/
protected function load(string $file, string $locale, bool $return = false)
{
if (! array_key_exists($locale, $this->loadedFiles)) {
$this->loadedFiles[$locale] = [];
}
if (in_array($file, $this->loadedFiles[$locale], true)) {
// Ne le chargez pas plus d'une fois.
return [];
}
if (! array_key_exists($locale, $this->language)) {
$this->language[$locale] = [];
}
if (! array_key_exists($file, $this->language[$locale])) {
$this->language[$locale][$file] = [];
}
$path = "Translations/{$locale}/{$file}.php";
$lang = $this->requireFile($path);
if ($return) {
return $lang;
}
$this->loadedFiles[$locale][] = $file;
// Fusionner notre chaîne
$this->language[$locale][$file] = $lang;
}
/**
* Une méthode simple pour inclure des fichiers qui peuvent être remplacés lors des tests.
*/
protected function requireFile(string $path): array
{
$files = $this->locator()->search($path, 'php', false);
$strings = [];
foreach ($files as $file) {
// Sur certains systèmes d'exploitation,
// nous voyions des échecs sur cette commande renvoyant un booléen au lieu d'un tableau f pendant les tests,
// nous avons donc supprimé le require_once pour l'instant.
if (is_file($file)) {
$strings[] = require $file;
}
}
if (isset($strings[1])) {
$string = array_shift($strings);
$strings = array_replace_recursive($string, ...$strings);
} elseif (isset($strings[0])) {
$strings = $strings[0];
}
return $strings;
}
protected function locator(): LocatorInterface
{
if (null !== $this->locator) {
return $this->locator;
}
$autoloader = new Autoloader([
'psr4' => [__NAMESPACE__ => __DIR__],
]);
return $this->locator = new Locator($autoloader->initialize());
}
}