TranslatorManager::doChoice()   C
last analyzed

Complexity

Conditions 14
Paths 36

Size

Total Lines 44
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 14.1691

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 44
ccs 19
cts 21
cp 0.9048
rs 6.2666
c 0
b 0
f 0
cc 14
nc 36
nop 3
crap 14.1691

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Ubiquity\translation;
4
5
use Ubiquity\exceptions\CacheException;
6
use Ubiquity\log\Logger;
7
use Ubiquity\translation\loader\ArrayLoader;
8
use Ubiquity\utils\base\UFileSystem;
9
use Ubiquity\utils\http\URequest;
10
11
/**
12
 * Manage translations.
13
 * Use the start method to start the Manager, after starting the cache manager
14
 * Ubiquity\translation$TranslatorManager
15
 * This class is part of Ubiquity
16
 *
17
 * @author jcheron <[email protected]>
18
 * @version 1.0.6
19
 */
20
class TranslatorManager {
21
	protected static $locale;
22
	protected static $loader;
23
	protected static $catalogues;
24
	protected static $fallbackLocale;
25
26
	/**
27
	 *
28
	 * @param callable $callback
29
	 * @param string $id
30
	 * @param array $parameters
31
	 * @param string $domain
32
	 * @param string $locale
33
	 * @return string
34
	 */
35 4
	protected static function transCallable($callback, $id, array $parameters = array (), $domain = null, $locale = null) {
36 4
		if (null === $domain) {
37
			$domain = 'messages';
38
		}
39 4
		$id = ( string ) $id;
40 4
		$catalogue = self::getCatalogue ( $locale );
41 4
		if ($catalogue === false) {
42 1
			if (isset ( self::$fallbackLocale ) && $locale !== self::$fallbackLocale) {
43
				self::setLocale ( self::$fallbackLocale );
44
				Logger::warn ( 'Translation', 'Locale ' . $locale . ' not found, set active locale to ' . self::$locale );
45
				return self::trans ( $id, $parameters, $domain, self::$locale );
46
			} else {
47 1
				Logger::error ( 'Translation', 'Locale not found, no valid fallbackLocale specified' );
48 1
				return $id;
49
			}
50
		}
51 4
		$transId = self::getTransId ( $id, $domain );
52 4
		if (isset ( $catalogue [$transId] )) {
53 4
			return $callback ( $catalogue [$transId], $parameters );
54
		} elseif (self::$fallbackLocale !== null && $locale !== self::$fallbackLocale) {
55
			Logger::warn ( 'Translation', 'Translation not found for ' . $id . '. Switch to fallbackLocale ' . self::$fallbackLocale );
56
			return self::trans ( $id, $parameters, $domain, self::$fallbackLocale );
57
		} else {
58
			Logger::warn ( 'Translation', 'Translation not found for ' . $id . '. in locales.' );
59
			return $id;
60
		}
61
	}
62
63
	/**
64
	 * Inspired by \Symfony\Contracts\Translation\TranslatorTrait$trans
65
	 *
66
	 * @param string $message
67
	 * @param array $choice
68
	 * @param array $parameters
69
	 * @return string
70
	 */
71 1
	protected static function doChoice($message, array $choice, array $parameters = [ ]) {
72 1
		$message = ( string ) $message;
73
74 1
		$number = ( float ) current ( $choice );
75 1
		$parameters = $parameters + $choice;
76 1
		$parts = [ ];
77 1
		if (preg_match ( '/^\|++$/', $message )) {
78
			$parts = explode ( '|', $message );
79 1
		} elseif (preg_match_all ( '/(?:\|\||[^\|])++/', $message, $matches )) {
80 1
			$parts = $matches [0];
81
		}
82
		$intervalRegexp = <<<'EOF'
83
		/^(?P<interval>
84
		    ({\s*
85
		        (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
86
		    \s*})
87
		        |
88
		    (?P<left_delimiter>[\[\]])
89
		        \s*
90
		        (?P<left>-Inf|\-?\d+(\.\d+)?)
91
		        \s*,\s*
92
		        (?P<right>\+?Inf|\-?\d+(\.\d+)?)
93
		        \s*
94
		    (?P<right_delimiter>[\[\]])
95
		)\s*(?P<message>.*?)$/xs
96
		EOF;
97 1
		foreach ( $parts as $part ) {
98 1
			$part = trim ( str_replace ( '||', '|', $part ) );
99 1
			if (preg_match ( $intervalRegexp, $part, $matches )) {
100 1
				if ($matches [2]) {
101 1
					foreach ( explode ( ',', $matches [3] ) as $n ) {
102 1
						if ($number == $n) {
103 1
							return self::replaceParams ( $matches ['message'], $parameters );
104
						}
105
					}
106
				} else {
107 1
					$leftNumber = '-Inf' === $matches ['left'] ? - INF : ( float ) $matches ['left'];
108 1
					$rightNumber = \is_numeric ( $matches ['right'] ) ? ( float ) $matches ['right'] : INF;
109 1
					if (('[' === $matches ['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber) && (']' === $matches ['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)) {
110 1
						return self::replaceParams ( $matches ['message'], $parameters );
111
					}
112
				}
113
			} else {
114
				return self::replaceParams ( $message, $parameters );
115
			}
116
		}
117
	}
118
119 4
	protected static function replaceParams($trans, array $parameters = array ()) {
120 4
		foreach ( $parameters as $k => $v ) {
121 1
			$trans = str_replace ( '%' . $k . '%', $v, $trans );
122
		}
123 4
		return $trans;
124
	}
125
126 4
	protected static function getTransId($id, $domain) {
127 4
		return $domain . '.' . $id;
128
	}
129
130 1
	protected static function assertValidLocale($locale) {
131 1
		if (1 !== preg_match ( '/^[a-z0-9@_\\.\\-]*$/i', $locale )) {
132
			throw new \InvalidArgumentException ( sprintf ( 'Invalid "%s" locale.', $locale ) );
133
		}
134
	}
135
136 1
	public static function isValidLocale($locale) {
137 1
		return (1 === preg_match ( '/^[a-z0-9@_\\.\\-]*$/i', $locale ));
138
	}
139
140
	/**
141
	 * Starts the translator manager.
142
	 * This operation must be performed after the CacheManager has been started
143
	 *
144
	 * @param string $locale The active locale
145
	 * @param string $fallbackLocale The fallback locale
146
	 * @param string $rootDir The root dir for translation (default: appRoot/translations)
147
	 */
148 5
	public static function start($locale = 'en_EN', $fallbackLocale = null, $rootDir = null) {
149 5
		self::$locale = $locale;
150 5
		self::$fallbackLocale = $fallbackLocale;
151 5
		self::setRootDir ( $rootDir );
152
	}
153
154
	/**
155
	 * Defines the active locale
156
	 *
157
	 * @param string $locale
158
	 */
159
	public static function setLocale($locale) {
160
		self::assertValidLocale ( $locale );
161
		self::$locale = $locale;
162
	}
163
164
	/**
165
	 * Sets the complete directory for translations.
166
	 * Default: appRoot/translations
167
	 *
168
	 * @param string $rootDir
169
	 */
170 5
	public static function setRootDir($rootDir = null) {
171 5
		if (! isset ( $rootDir )) {
172 5
			$rootDir = \ROOT . \DS . 'translations';
173
		}
174 5
		self::$loader = new ArrayLoader ( $rootDir );
175
	}
176
177
	/**
178
	 * Returns the active locale
179
	 *
180
	 * @return string
181
	 */
182 4
	public static function getLocale() {
183 4
		return self::$locale;
184
	}
185
186
	/**
187
	 * Returns a translation corresponding to an id, using eventually some parameters
188
	 *
189
	 * @param string $id
190
	 * @param array $parameters
191
	 * @param string $domain
192
	 * @param string $locale
193
	 * @return string
194
	 */
195 4
	public static function trans($id, array $parameters = array (), $domain = null, $locale = null) {
196 4
		return self::transCallable ( function ($trans, $parameters) {
197 4
			return self::replaceParams ( $trans, $parameters );
198 4
		}, $id, $parameters, $domain, $locale );
199
	}
200
201
	/**
202
	 * Returns a translation with choices corresponding to an id, using eventually some parameters
203
	 *
204
	 * @param string $id
205
	 * @param array $choice
206
	 * @param array $parameters
207
	 * @param string $domain
208
	 * @param string $locale
209
	 * @return string
210
	 */
211 1
	public static function transChoice($id, array $choice, array $parameters = array (), $domain = null, $locale = null) {
212 1
		return self::transCallable ( function ($message, $parameters) use ($choice) {
213 1
			return self::doChoice ( $message, $choice, $parameters );
214 1
		}, $id, $parameters, $domain, $locale );
215
	}
216
217
	/**
218
	 * Returns the translations catalog of the locale
219
	 *
220
	 * @param string $locale
221
	 * @return array|boolean
222
	 */
223 5
	public static function getCatalogue(&$locale = null) {
224 5
		if (null === $locale) {
225 4
			$locale = self::getLocale ();
226
		} else {
227 1
			self::assertValidLocale ( $locale );
228
		}
229 5
		if (! isset ( self::$catalogues [$locale] )) {
230 3
			self::loadCatalogue ( $locale );
231
		}
232 5
		return self::$catalogues [$locale] ?? false;
233
	}
234
235
	/**
236
	 * Loads a catalog for a locale
237
	 *
238
	 * @param string $locale
239
	 */
240 3
	public static function loadCatalogue($locale = null) {
241 3
		self::$catalogues [$locale] = self::$loader->load ( $locale );
242
	}
243
244
	/**
245
	 * Returns the fallbackLocale
246
	 *
247
	 * @return string
248
	 */
249
	public static function getFallbackLocale() {
250
		return self::$fallbackLocale;
251
	}
252
253
	/**
254
	 * Sets the fallbackLocale
255
	 *
256
	 * @param string $fallbackLocale
257
	 */
258
	public static function setFallbackLocale($fallbackLocale) {
259
		self::$fallbackLocale = $fallbackLocale;
260
	}
261
262
	/**
263
	 * Clears the translations cache
264
	 */
265
	public static function clearCache() {
266
		self::$loader->clearCache ( '*' );
267
	}
268
269
	/**
270
	 * Clears the $locale translations cache
271
	 */
272 1
	public static function clearLocaleCache($locale) {
273 1
		self::$loader->clearCache ( $locale );
274
	}
275
276
	/**
277
	 * Returns the available locales
278
	 *
279
	 * @return string[]
280
	 */
281 1
	public static function getLocales() {
282 1
		$locales = [ ];
283 1
		$dirs = \glob ( self::getRootDir () . \DS . '*', GLOB_ONLYDIR );
284 1
		foreach ( $dirs as $dir ) {
285 1
			$locales [] = \basename ( $dir );
286
		}
287 1
		return $locales;
288
	}
289
290
	/**
291
	 * Returns the existing domains in $locale
292
	 *
293
	 * @param string $locale
294
	 * @return array
295
	 */
296 1
	public static function getDomains($locale) {
297 1
		$catalog = new MessagesCatalog ( $locale, self::$loader );
298 1
		return $catalog->getDomains ();
299
	}
300
301
	/**
302
	 * Returns translations root dir
303
	 *
304
	 * @return string
305
	 */
306 1
	public static function getRootDir() {
307 1
		return self::$loader->getRootDir ();
308
	}
309
310
	/**
311
	 * Returns the active loader
312
	 *
313
	 * @return \Ubiquity\translation\loader\ArrayLoader
314
	 */
315 1
	public static function getLoader() {
316 1
		return self::$loader;
317
	}
318
319
	/**
320
	 * Returns all catalogs
321
	 *
322
	 * @return mixed
323
	 */
324
	public static function getCatalogues() {
325
		return self::$catalogues;
326
	}
327
328
	/**
329
	 * Creates default translations root directory
330
	 *
331
	 * @param string $rootDir
332
	 * @return string[]
333
	 */
334
	public static function initialize($rootDir = null) {
335
		$locale = URequest::getDefaultLanguage ();
336
		self::createLocale ( self::fixLocale ( $locale ), $rootDir );
337
		return self::getLocales ();
338
	}
339
340 2
	public static function fixLocale($language) {
341 2
		return \str_replace ( [ '-','.' ], '_', $language );
342
	}
343
344
	/**
345
	 * Creates the locale folder in translations root directory
346
	 *
347
	 * @param string $locale
348
	 * @param string $rootDir
349
	 */
350 1
	public static function createLocale($locale, $rootDir = null) {
351 1
		self::setRootDir ( $rootDir );
352 1
		UFileSystem::safeMkdir ( self::getRootDir () . \DS . $locale );
353
	}
354
355
	/**
356
	 * Creates a new domain in $locale.
357
	 * throws a CacheException if TranslatorManager is not started
358
	 *
359
	 * @param string $locale
360
	 * @param string $domain
361
	 * @param array|null $defaultValues
362
	 * @throws CacheException
363
	 * @return \Ubiquity\translation\MessagesDomain|boolean
364
	 */
365 1
	public static function createDomain($locale, $domain, $defaultValues = null) {
366 1
		if (isset ( self::$loader )) {
367 1
			$domains = self::getDomains ( $locale );
368 1
			if (\array_search ( $domain, $domains ) === false) {
369 1
				$dom = new MessagesDomain ( $locale, self::$loader, $domain );
370 1
				if (\is_array ( $defaultValues )) {
371 1
					$dom->setMessages ( $defaultValues );
372
				}
373 1
				$dom->store ();
374 1
				return $dom;
375
			}
376
			return false;
377
		}
378
		throw new CacheException ( 'TranslatorManager is not started!' );
379
	}
380
381
	/**
382
	 * Check if the cache exists for a $domain in $locale
383
	 *
384
	 * @param string $locale
385
	 * @param string $domain
386
	 * @return boolean
387
	 */
388 1
	public static function cacheExist($locale, $domain = '*') {
389 1
		return self::$loader->cacheExists ( $locale, $domain );
390
	}
391
}
392