1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Kdyby (http://www.kdyby.org) |
5
|
|
|
* |
6
|
|
|
* Copyright (c) 2008 Filip Procházka ([email protected]) |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the file license.txt that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Kdyby\Translation\DI; |
12
|
|
|
|
13
|
|
|
use Kdyby\Console\DI\ConsoleExtension; |
14
|
|
|
use Kdyby\Monolog\Logger as KdybyLogger; |
15
|
|
|
use Kdyby\Translation\Caching\PhpFileStorage; |
16
|
|
|
use Kdyby\Translation\CatalogueCompiler; |
17
|
|
|
use Kdyby\Translation\CatalogueFactory; |
18
|
|
|
use Kdyby\Translation\Console\ExtractCommand; |
19
|
|
|
use Kdyby\Translation\Diagnostics\Panel; |
20
|
|
|
use Kdyby\Translation\FallbackResolver; |
21
|
|
|
use Kdyby\Translation\IUserLocaleResolver; |
22
|
|
|
use Kdyby\Translation\Latte\TranslateMacros; |
23
|
|
|
use Kdyby\Translation\LocaleResolver\AcceptHeaderResolver; |
24
|
|
|
use Kdyby\Translation\LocaleResolver\ChainResolver; |
25
|
|
|
use Kdyby\Translation\LocaleResolver\LocaleParamResolver; |
26
|
|
|
use Kdyby\Translation\LocaleResolver\SessionResolver; |
27
|
|
|
use Kdyby\Translation\TemplateHelpers; |
28
|
|
|
use Kdyby\Translation\TranslationLoader; |
29
|
|
|
use Kdyby\Translation\Translator as KdybyTranslator; |
30
|
|
|
use Latte\Engine as LatteEngine; |
31
|
|
|
use Nette\Application\Application; |
32
|
|
|
use Nette\Bridges\ApplicationLatte\ILatteFactory; |
33
|
|
|
use Nette\Configurator; |
34
|
|
|
use Nette\DI\Compiler; |
35
|
|
|
use Nette\DI\Definitions\FactoryDefinition; |
36
|
|
|
use Nette\DI\Helpers; |
37
|
|
|
use Nette\DI\Statement; |
38
|
|
|
use Nette\PhpGenerator\ClassType as ClassTypeGenerator; |
39
|
|
|
use Nette\PhpGenerator\PhpLiteral; |
40
|
|
|
use Nette\Reflection\ClassType as ReflectionClassType; |
41
|
|
|
use Nette\Schema\Expect; |
42
|
|
|
use Nette\Schema\Schema; |
43
|
|
|
use Nette\Utils\Callback; |
44
|
|
|
use Nette\Utils\Finder; |
45
|
|
|
use Nette\Utils\Validators; |
46
|
|
|
use Symfony\Component\Translation\Extractor\ChainExtractor; |
47
|
|
|
use Symfony\Component\Translation\Formatter\IntlFormatter; |
48
|
|
|
use Symfony\Component\Translation\Formatter\IntlFormatterInterface; |
49
|
|
|
use Symfony\Component\Translation\Formatter\MessageFormatter; |
50
|
|
|
use Symfony\Component\Translation\Formatter\MessageFormatterInterface; |
51
|
|
|
use Symfony\Component\Translation\Loader\LoaderInterface; |
52
|
|
|
use Symfony\Component\Translation\MessageSelector; |
53
|
|
|
use Symfony\Component\Translation\Writer\TranslationWriter; |
54
|
|
|
use Tracy\Debugger; |
55
|
|
|
use Tracy\IBarPanel; |
56
|
|
|
|
57
|
|
|
class TranslationExtension extends \Nette\DI\CompilerExtension |
58
|
|
|
{ |
59
|
|
|
|
60
|
|
|
use \Kdyby\StrictObjects\Scream; |
61
|
|
|
|
62
|
|
|
/** @deprecated */ |
63
|
|
|
const LOADER_TAG = self::TAG_LOADER; |
64
|
|
|
/** @deprecated */ |
65
|
|
|
const DUMPER_TAG = self::TAG_DUMPER; |
66
|
|
|
/** @deprecated */ |
67
|
|
|
const EXTRACTOR_TAG = self::TAG_EXTRACTOR; |
68
|
|
|
|
69
|
|
|
const TAG_LOADER = 'translation.loader'; |
70
|
|
|
const TAG_DUMPER = 'translation.dumper'; |
71
|
|
|
const TAG_EXTRACTOR = 'translation.extractor'; |
72
|
|
|
|
73
|
|
|
const RESOLVER_REQUEST = 'request'; |
74
|
|
|
const RESOLVER_HEADER = 'header'; |
75
|
|
|
const RESOLVER_SESSION = 'session'; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var mixed[] |
79
|
|
|
*/ |
80
|
|
|
public $defaults = [ |
81
|
|
|
'whitelist' => NULL, // array('cs', 'en'), |
82
|
|
|
'default' => 'en', |
83
|
|
|
'logging' => NULL, // TRUE for psr/log, or string for kdyby/monolog channel |
84
|
|
|
// 'fallback' => array('en_US', 'en'), // using custom merge strategy becase Nette's config merger appends lists of values |
85
|
|
|
'dirs' => ['%appDir%/lang', '%appDir%/locale'], |
86
|
|
|
'cache' => PhpFileStorage::class, |
87
|
|
|
'debugger' => '%debugMode%', |
88
|
|
|
'resolvers' => [ |
89
|
|
|
self::RESOLVER_SESSION => FALSE, |
90
|
|
|
self::RESOLVER_REQUEST => TRUE, |
91
|
|
|
self::RESOLVER_HEADER => TRUE, |
92
|
|
|
], |
93
|
|
|
'loaders' => [], |
94
|
|
|
]; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @var array |
98
|
|
|
*/ |
99
|
|
|
private $loaders; |
100
|
|
|
|
101
|
|
|
public function getConfigSchema(): Schema |
102
|
|
|
{ |
103
|
|
|
return Expect::structure([ |
104
|
|
|
'whitelist' => Expect::anyOf(Expect::arrayOf('string'), NULL), |
105
|
|
|
'default' => Expect::string('en'), |
106
|
|
|
'logging' => Expect::anyOf(Expect::string(), Expect::bool()), |
107
|
|
|
'fallback' => Expect::arrayOf('string')->default(['en_US']), |
108
|
|
|
'dirs' => Expect::arrayOf('string')->default(['%appDir%/lang', '%appDir%/locale']), |
109
|
|
|
'cache' => Expect::string(PhpFileStorage::class), |
110
|
|
|
'debugger' => Expect::bool(FALSE), |
111
|
|
|
'resolvers' => Expect::array()->default([ |
112
|
|
|
self::RESOLVER_SESSION => FALSE, |
113
|
|
|
self::RESOLVER_REQUEST => TRUE, |
114
|
|
|
self::RESOLVER_HEADER => TRUE, |
115
|
|
|
]), |
116
|
|
|
'loaders' => Expect::array(), |
117
|
|
|
])->castTo('array'); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function loadConfiguration() |
121
|
|
|
{ |
122
|
|
|
$this->loaders = []; |
123
|
|
|
|
124
|
|
|
$builder = $this->getContainerBuilder(); |
125
|
|
|
/** @var array $config */ |
126
|
|
|
$config = $this->config; |
127
|
|
|
$config['cache'] = new Statement($config['cache'], [dirname(Helpers::expand('%tempDir%/cache', $builder->parameters))]); |
128
|
|
|
|
129
|
|
|
$translator = $builder->addDefinition($this->prefix('default')) |
130
|
|
|
->setFactory(KdybyTranslator::class, [$this->prefix('@userLocaleResolver')]) |
131
|
|
|
->addSetup('?->setTranslator(?)', [$this->prefix('@userLocaleResolver.param'), '@self']) |
132
|
|
|
->addSetup('setDefaultLocale', [$config['default']]) |
133
|
|
|
->addSetup('setLocaleWhitelist', [$config['whitelist']]); |
134
|
|
|
|
135
|
|
|
Validators::assertField($config, 'fallback', 'list'); |
136
|
|
|
$translator->addSetup('setFallbackLocales', [$config['fallback']]); |
137
|
|
|
|
138
|
|
|
$catalogueCompiler = $builder->addDefinition($this->prefix('catalogueCompiler')) |
139
|
|
|
->setFactory(CatalogueCompiler::class, self::filterArgs($config['cache'])); |
140
|
|
|
|
141
|
|
|
if ($config['debugger'] && interface_exists(IBarPanel::class)) { |
142
|
|
|
$builder->addDefinition($this->prefix('panel')) |
143
|
|
|
->setFactory(Panel::class, [dirname(Helpers::expand('%appDir%', $builder->parameters))]) |
144
|
|
|
->addSetup('setLocaleWhitelist', [$config['whitelist']]); |
145
|
|
|
|
146
|
|
|
$translator->addSetup('?->register(?)', [$this->prefix('@panel'), '@self']); |
147
|
|
|
$catalogueCompiler->addSetup('enableDebugMode'); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
$this->loadLocaleResolver($config); |
151
|
|
|
|
152
|
|
|
$builder->addDefinition($this->prefix('helpers')) |
153
|
|
|
->setClass(TemplateHelpers::class) |
154
|
|
|
->setFactory($this->prefix('@default') . '::createTemplateHelpers'); |
155
|
|
|
|
156
|
|
|
$builder->addDefinition($this->prefix('fallbackResolver')) |
157
|
|
|
->setClass(FallbackResolver::class); |
158
|
|
|
|
159
|
|
|
$builder->addDefinition($this->prefix('catalogueFactory')) |
160
|
|
|
->setClass(CatalogueFactory::class); |
161
|
|
|
|
162
|
|
|
$builder->addDefinition($this->prefix('selector')) |
163
|
|
|
->setClass(MessageSelector::class); |
164
|
|
|
|
165
|
|
|
if (interface_exists(IntlFormatterInterface::class)) { |
166
|
|
|
$builder->addDefinition($this->prefix('intlFormatter')) |
167
|
|
|
->setType(IntlFormatterInterface::class) |
168
|
|
|
->setFactory(IntlFormatter::class); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$builder->addDefinition($this->prefix('formatter')) |
172
|
|
|
->setType(MessageFormatterInterface::class) |
173
|
|
|
->setFactory(MessageFormatter::class); |
174
|
|
|
|
175
|
|
|
$builder->addDefinition($this->prefix('extractor')) |
176
|
|
|
->setClass(ChainExtractor::class); |
177
|
|
|
|
178
|
|
|
$this->loadExtractors(); |
179
|
|
|
|
180
|
|
|
$builder->addDefinition($this->prefix('writer')) |
181
|
|
|
->setClass(TranslationWriter::class); |
182
|
|
|
|
183
|
|
|
$this->loadDumpers(); |
184
|
|
|
|
185
|
|
|
$builder->addDefinition($this->prefix('loader')) |
186
|
|
|
->setClass(TranslationLoader::class); |
187
|
|
|
|
188
|
|
|
$loaders = $this->loadFromFile(__DIR__ . '/config/loaders.neon'); |
189
|
|
|
$this->loadLoaders($loaders, $config['loaders'] ?: array_keys($loaders)); |
190
|
|
|
|
191
|
|
|
if ($this->isRegisteredConsoleExtension()) { |
192
|
|
|
$this->loadConsole($config); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
protected function loadLocaleResolver(array $config) |
197
|
|
|
{ |
198
|
|
|
$builder = $this->getContainerBuilder(); |
199
|
|
|
|
200
|
|
|
$builder->addDefinition($this->prefix('userLocaleResolver.param')) |
201
|
|
|
->setClass(LocaleParamResolver::class) |
202
|
|
|
->setAutowired(FALSE); |
203
|
|
|
|
204
|
|
|
$builder->addDefinition($this->prefix('userLocaleResolver.acceptHeader')) |
205
|
|
|
->setClass(AcceptHeaderResolver::class); |
206
|
|
|
|
207
|
|
|
$builder->addDefinition($this->prefix('userLocaleResolver.session')) |
208
|
|
|
->setClass(SessionResolver::class); |
209
|
|
|
|
210
|
|
|
$chain = $builder->addDefinition($this->prefix('userLocaleResolver')) |
211
|
|
|
->setClass(IUserLocaleResolver::class) |
212
|
|
|
->setFactory(ChainResolver::class); |
213
|
|
|
|
214
|
|
|
$resolvers = []; |
215
|
|
View Code Duplication |
if ($config['resolvers'][self::RESOLVER_HEADER]) { |
|
|
|
|
216
|
|
|
$resolvers[] = $this->prefix('@userLocaleResolver.acceptHeader'); |
217
|
|
|
$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.acceptHeader')]); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
View Code Duplication |
if ($config['resolvers'][self::RESOLVER_REQUEST]) { |
|
|
|
|
221
|
|
|
$resolvers[] = $this->prefix('@userLocaleResolver.param'); |
222
|
|
|
$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.param')]); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
View Code Duplication |
if ($config['resolvers'][self::RESOLVER_SESSION]) { |
|
|
|
|
226
|
|
|
$resolvers[] = $this->prefix('@userLocaleResolver.session'); |
227
|
|
|
$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.session')]); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if ($config['debugger'] && interface_exists(IBarPanel::class)) { |
231
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $panel */ |
232
|
|
|
$panel = $builder->getDefinition($this->prefix('panel')); |
233
|
|
|
$panel->addSetup('setLocaleResolvers', [array_reverse($resolvers)]); |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
protected function loadConsole(array $config) |
238
|
|
|
{ |
239
|
|
|
$builder = $this->getContainerBuilder(); |
240
|
|
|
|
241
|
|
|
Validators::assertField($config, 'dirs', 'list'); |
242
|
|
|
$builder->addDefinition($this->prefix('console.extract')) |
243
|
|
|
->setFactory(ExtractCommand::class) |
244
|
|
|
->addSetup('$defaultOutputDir', [reset($config['dirs'])]) |
245
|
|
|
->addTag(ConsoleExtension::TAG_COMMAND, 'latte'); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
View Code Duplication |
protected function loadDumpers() |
|
|
|
|
249
|
|
|
{ |
250
|
|
|
$builder = $this->getContainerBuilder(); |
251
|
|
|
|
252
|
|
|
foreach ($this->loadFromFile(__DIR__ . '/config/dumpers.neon') as $format => $class) { |
253
|
|
|
$builder->addDefinition($this->prefix('dumper.' . $format)) |
254
|
|
|
->setClass($class) |
255
|
|
|
->addTag(self::TAG_DUMPER, $format); |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
protected function loadLoaders(array $loaders, array $allowed) |
260
|
|
|
{ |
261
|
|
|
$builder = $this->getContainerBuilder(); |
262
|
|
|
|
263
|
|
|
foreach ($loaders as $format => $class) { |
264
|
|
|
if (array_search($format, $allowed) === FALSE) { |
265
|
|
|
continue; |
266
|
|
|
} |
267
|
|
|
$builder->addDefinition($this->prefix('loader.' . $format)) |
268
|
|
|
->setClass($class) |
269
|
|
|
->addTag(self::TAG_LOADER, $format); |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
|
273
|
|
View Code Duplication |
protected function loadExtractors() |
|
|
|
|
274
|
|
|
{ |
275
|
|
|
$builder = $this->getContainerBuilder(); |
276
|
|
|
|
277
|
|
|
foreach ($this->loadFromFile(__DIR__ . '/config/extractors.neon') as $format => $class) { |
278
|
|
|
$builder->addDefinition($this->prefix('extractor.' . $format)) |
279
|
|
|
->setClass($class) |
280
|
|
|
->addTag(self::TAG_EXTRACTOR, $format); |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
public function beforeCompile() |
285
|
|
|
{ |
286
|
|
|
$builder = $this->getContainerBuilder(); |
287
|
|
|
/** @var array $config */ |
288
|
|
|
$config = $this->config; |
289
|
|
|
|
290
|
|
|
$this->beforeCompileLogging($config); |
291
|
|
|
|
292
|
|
|
$registerToLatte = function (FactoryDefinition $def) { |
293
|
|
|
$def->getResultDefinition()->addSetup('?->onCompile[] = function($engine) { ?::install($engine->getCompiler()); }', ['@self', new PhpLiteral(TranslateMacros::class)]); |
294
|
|
|
|
295
|
|
|
$def->getResultDefinition() |
296
|
|
|
->addSetup('addProvider', ['translator', $this->prefix('@default')]) |
297
|
|
|
->addSetup('addFilter', ['translate', [$this->prefix('@helpers'), 'translateFilterAware']]); |
298
|
|
|
}; |
299
|
|
|
|
300
|
|
|
$latteFactoryService = $builder->getByType(ILatteFactory::class); |
301
|
|
|
if (!$latteFactoryService || !self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), LatteEngine::class)) { |
302
|
|
|
$latteFactoryService = 'nette.latteFactory'; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
if ($builder->hasDefinition($latteFactoryService) && self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), ILatteFactory::class)) { |
306
|
|
|
$registerToLatte($builder->getDefinition($latteFactoryService)); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
if ($builder->hasDefinition('nette.latte')) { |
310
|
|
|
$registerToLatte($builder->getDefinition('nette.latte')); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
$applicationService = $builder->getByType(Application::class) ?: 'application'; |
314
|
|
|
if ($builder->hasDefinition($applicationService)) { |
315
|
|
|
|
316
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $applicationServiceDefinition */ |
317
|
|
|
$applicationServiceDefinition = $builder->getDefinition($applicationService); |
318
|
|
|
$applicationServiceDefinition |
319
|
|
|
->addSetup('$service->onRequest[] = ?', [[$this->prefix('@userLocaleResolver.param'), 'onRequest']]); |
320
|
|
|
|
321
|
|
|
if ($config['debugger'] && interface_exists(IBarPanel::class)) { |
322
|
|
|
$applicationServiceDefinition |
323
|
|
|
->addSetup('$self = $this; $service->onStartup[] = function () use ($self) { $self->getService(?); }', [$this->prefix('default')]) |
324
|
|
|
->addSetup('$service->onRequest[] = ?', [[$this->prefix('@panel'), 'onRequest']]); |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
if (class_exists(Debugger::class)) { |
329
|
|
|
Panel::registerBluescreen(); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $extractor */ |
333
|
|
|
$extractor = $builder->getDefinition($this->prefix('extractor')); |
334
|
|
View Code Duplication |
foreach ($builder->findByTag(self::TAG_EXTRACTOR) as $extractorId => $meta) { |
|
|
|
|
335
|
|
|
Validators::assert($meta, 'string:2..'); |
336
|
|
|
|
337
|
|
|
$extractor->addSetup('addExtractor', [$meta, '@' . $extractorId]); |
338
|
|
|
|
339
|
|
|
$builder->getDefinition($extractorId)->setAutowired(FALSE); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $writer */ |
343
|
|
|
$writer = $builder->getDefinition($this->prefix('writer')); |
344
|
|
View Code Duplication |
foreach ($builder->findByTag(self::TAG_DUMPER) as $dumperId => $meta) { |
|
|
|
|
345
|
|
|
Validators::assert($meta, 'string:2..'); |
346
|
|
|
|
347
|
|
|
$writer->addSetup('addDumper', [$meta, '@' . $dumperId]); |
348
|
|
|
|
349
|
|
|
$builder->getDefinition($dumperId)->setAutowired(FALSE); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
$this->loaders = []; |
353
|
|
|
foreach ($builder->findByTag(self::TAG_LOADER) as $loaderId => $meta) { |
354
|
|
|
Validators::assert($meta, 'string:2..'); |
355
|
|
|
$builder->getDefinition($loaderId)->setAutowired(FALSE); |
356
|
|
|
$this->loaders[$meta] = $loaderId; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $loaderDefinition */ |
360
|
|
|
$loaderDefinition = $builder->getDefinition($this->prefix('loader')); |
361
|
|
|
$loaderDefinition->addSetup('injectServiceIds', [$this->loaders]); |
362
|
|
|
|
363
|
|
|
foreach ($this->compiler->getExtensions() as $extension) { |
364
|
|
|
if (!$extension instanceof ITranslationProvider) { |
365
|
|
|
continue; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
$config['dirs'] = array_merge($config['dirs'], array_values($extension->getTranslationResources())); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
$config['dirs'] = array_map(function ($dir) use ($builder) { |
372
|
|
|
return str_replace((DIRECTORY_SEPARATOR === '/') ? '\\' : '/', DIRECTORY_SEPARATOR, Helpers::expand($dir, $builder->parameters)); |
373
|
|
|
}, $config['dirs']); |
374
|
|
|
|
375
|
|
|
$dirs = array_values(array_filter($config['dirs'], Callback::closure('is_dir'))); |
376
|
|
|
if (count($dirs) > 0) { |
377
|
|
|
foreach ($dirs as $dir) { |
378
|
|
|
$builder->addDependency($dir); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
$this->loadResourcesFromDirs($dirs); |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
protected function beforeCompileLogging(array $config) |
386
|
|
|
{ |
387
|
|
|
$builder = $this->getContainerBuilder(); |
388
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $translator */ |
389
|
|
|
$translator = $builder->getDefinition($this->prefix('default')); |
390
|
|
|
|
391
|
|
|
if ($config['logging'] === TRUE) { |
392
|
|
|
$translator->addSetup('injectPsrLogger'); |
393
|
|
|
|
394
|
|
|
} elseif (is_string($config['logging'])) { // channel for kdyby/monolog |
395
|
|
|
$translator->addSetup('injectPsrLogger', [ |
396
|
|
|
new Statement(sprintf('@%s::channel', KdybyLogger::class), [$config['logging']]), |
397
|
|
|
]); |
398
|
|
|
|
399
|
|
|
} elseif ($config['logging'] !== NULL) { |
400
|
|
|
throw new \Kdyby\Translation\InvalidArgumentException(sprintf( |
401
|
|
|
'Invalid config option for logger. Valid are TRUE for general psr/log or string for kdyby/monolog channel, but %s was given', |
402
|
|
|
$config['logging'] |
403
|
|
|
)); |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
protected function loadResourcesFromDirs($dirs) |
408
|
|
|
{ |
409
|
|
|
$builder = $this->getContainerBuilder(); |
410
|
|
|
$config = $this->config; |
411
|
|
|
|
412
|
|
|
$whitelistRegexp = KdybyTranslator::buildWhitelistRegexp($config['whitelist']); |
413
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $translator */ |
414
|
|
|
$translator = $builder->getDefinition($this->prefix('default')); |
415
|
|
|
|
416
|
|
|
$mask = array_map(function ($value) { |
417
|
|
|
return '*.*.' . $value; |
418
|
|
|
}, array_keys($this->loaders)); |
419
|
|
|
|
420
|
|
|
foreach (Finder::findFiles($mask)->from($dirs) as $file) { |
421
|
|
|
/** @var \SplFileInfo $file */ |
422
|
|
|
if (!preg_match('~^(?P<domain>.*?)\.(?P<locale>[^\.]+)\.(?P<format>[^\.]+)$~', $file->getFilename(), $m)) { |
423
|
|
|
continue; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
if ($whitelistRegexp && !preg_match($whitelistRegexp, $m['locale']) && $builder->parameters['productionMode']) { |
|
|
|
|
427
|
|
|
continue; // ignore in production mode, there is no need to pass the ignored resources |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
$this->validateResource($m['format'], $file->getPathname(), $m['locale'], $m['domain']); |
431
|
|
|
$translator->addSetup('addResource', [$m['format'], $file->getPathname(), $m['locale'], $m['domain']]); |
432
|
|
|
$builder->addDependency($file->getPathname()); |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @param string $format |
438
|
|
|
* @param string $file |
439
|
|
|
* @param string $locale |
440
|
|
|
* @param string $domain |
441
|
|
|
*/ |
442
|
|
|
protected function validateResource($format, $file, $locale, $domain) |
443
|
|
|
{ |
444
|
|
|
$builder = $this->getContainerBuilder(); |
445
|
|
|
|
446
|
|
|
if (!isset($this->loaders[$format])) { |
447
|
|
|
return; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
try { |
451
|
|
|
/** @var \Nette\DI\Definitions\ServiceDefinition $def */ |
452
|
|
|
$def = $builder->getDefinition($this->loaders[$format]); |
453
|
|
|
$refl = ReflectionClassType::from($def->getEntity() ?: $def->getClass()); |
454
|
|
|
$method = $refl->getConstructor(); |
455
|
|
|
if ($method !== NULL && $method->getNumberOfRequiredParameters() > 1) { |
456
|
|
|
return; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
$loader = $refl->newInstance(); |
460
|
|
|
if (!$loader instanceof LoaderInterface) { |
|
|
|
|
461
|
|
|
return; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
} catch (\ReflectionException $e) { |
465
|
|
|
return; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
try { |
469
|
|
|
$loader->load($file, $locale, $domain); |
470
|
|
|
|
471
|
|
|
} catch (\Exception $e) { |
472
|
|
|
throw new \Kdyby\Translation\InvalidResourceException(sprintf('Resource %s is not valid and cannot be loaded.', $file), 0, $e); |
473
|
|
|
} |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
public function afterCompile(ClassTypeGenerator $class) |
477
|
|
|
{ |
478
|
|
|
$initialize = $class->getMethod('initialize'); |
479
|
|
|
if (class_exists(Debugger::class)) { |
480
|
|
|
$initialize->addBody('?::registerBluescreen();', [new PhpLiteral(Panel::class)]); |
481
|
|
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
private function isRegisteredConsoleExtension() |
485
|
|
|
{ |
486
|
|
|
foreach ($this->compiler->getExtensions() as $extension) { |
487
|
|
|
if ($extension instanceof ConsoleExtension) { |
|
|
|
|
488
|
|
|
return TRUE; |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
return FALSE; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* @param \Nette\Configurator $configurator |
497
|
|
|
*/ |
498
|
|
|
public static function register(Configurator $configurator) |
499
|
|
|
{ |
500
|
|
|
$configurator->onCompile[] = function ($config, Compiler $compiler) { |
501
|
|
|
$compiler->addExtension('translation', new TranslationExtension()); |
502
|
|
|
}; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* @param string|\stdClass $statement |
507
|
|
|
* @return \Nette\DI\Statement[] |
508
|
|
|
*/ |
509
|
|
|
protected static function filterArgs($statement) |
510
|
|
|
{ |
511
|
|
|
return Helpers::filterArguments([is_string($statement) ? new Statement($statement) : $statement]); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* @param string|NULL $class |
516
|
|
|
* @param string $type |
517
|
|
|
* @return bool |
518
|
|
|
*/ |
519
|
|
|
private static function isOfType($class, $type) |
520
|
|
|
{ |
521
|
|
|
return $class !== NULL && ($class === $type || is_subclass_of($class, $type)); |
|
|
|
|
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
} |
525
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.