| Total Complexity | 74 |
| Total Lines | 555 |
| Duplicated Lines | 0 % |
| Changes | 4 | ||
| Bugs | 0 | Features | 0 |
Complex classes like Lang often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Lang, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 9 | class Lang |
||
| 10 | { |
||
| 11 | /** |
||
| 12 | * Array of transliterations |
||
| 13 | * @var array |
||
| 14 | */ |
||
| 15 | private static $_transliterations; |
||
| 16 | |||
| 17 | /** |
||
| 18 | * Code of active language |
||
| 19 | * @var string |
||
| 20 | */ |
||
| 21 | private static $_lang; |
||
| 22 | |||
| 23 | /** |
||
| 24 | * Context information of all available languages |
||
| 25 | * @var array |
||
| 26 | */ |
||
| 27 | private static $_languages; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * Array of localized strings |
||
| 31 | * @var array |
||
| 32 | */ |
||
| 33 | private static $_dictionary; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Array of localized date and time strings |
||
| 37 | * @var array |
||
| 38 | */ |
||
| 39 | private static $_datetime_dictionary; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Get the current dictionary |
||
| 43 | * |
||
| 44 | * @return array |
||
| 45 | * Return the dictionary |
||
| 46 | */ |
||
| 47 | public static function Dictionary() |
||
| 48 | { |
||
| 49 | return self::$_dictionary; |
||
| 50 | } |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Get a list of either enabled or disabled languages. Example: |
||
| 54 | * |
||
| 55 | * array( |
||
| 56 | * [...] |
||
| 57 | * |
||
| 58 | * 'en' => array( |
||
| 59 | * 'name' => 'English', |
||
| 60 | * 'handle' => 'english', |
||
| 61 | * 'extensions' => array() |
||
| 62 | * ), |
||
| 63 | * |
||
| 64 | * 'it' => array( |
||
| 65 | * 'name' => 'Italiano', |
||
| 66 | * 'handle' => 'italian', |
||
| 67 | * 'extensions' => array( |
||
| 68 | * [...] |
||
| 69 | * ) |
||
| 70 | * ), |
||
| 71 | * |
||
| 72 | * [...] |
||
| 73 | * ) |
||
| 74 | * |
||
| 75 | * @see toolkit.Lang#createLanguage() |
||
| 76 | * @since Symphony 2.3 |
||
| 77 | * @return array |
||
| 78 | * Return an array of languages (both enabled and disabled) |
||
| 79 | */ |
||
| 80 | public static function Languages() |
||
| 81 | { |
||
| 82 | return self::$_languages; |
||
| 83 | } |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Get transliterations |
||
| 87 | * |
||
| 88 | * @return array |
||
| 89 | * Returns the transliterations array |
||
| 90 | */ |
||
| 91 | public static function Transliterations() |
||
| 94 | } |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Initialize transliterations, datetime dictionary and languages array. |
||
| 98 | */ |
||
| 99 | public static function initialize() |
||
| 100 | { |
||
| 101 | self::$_dictionary = array(); |
||
| 102 | |||
| 103 | // Load default datetime strings |
||
| 104 | if (empty(self::$_datetime_dictionary)) { |
||
| 105 | self::$_datetime_dictionary = include LANG . '/datetime.php'; |
||
|
|
|||
| 106 | } |
||
| 107 | |||
| 108 | // Load default transliterations |
||
| 109 | if (empty(self::$_transliterations)) { |
||
| 110 | self::$_transliterations = include LANG . '/transliterations.php'; |
||
| 111 | } |
||
| 112 | |||
| 113 | // Load default English language |
||
| 114 | if (empty(self::$_languages)) { |
||
| 115 | self::$_languages = self::createLanguage('en', 'English', 'english'); |
||
| 116 | } |
||
| 117 | |||
| 118 | // Fetch all available languages |
||
| 119 | self::fetch(); |
||
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Create an array of Language information for internal use. |
||
| 124 | * |
||
| 125 | * @since Symphony 2.3 |
||
| 126 | * @param string $code |
||
| 127 | * Language code, e. g. 'en' or 'pt-br' |
||
| 128 | * @param string $name |
||
| 129 | * Language name |
||
| 130 | * @param string $handle (optional) |
||
| 131 | * Handle for the given language, used to build a valid 'lang_$handle' extension's handle. |
||
| 132 | * Defaults to null. |
||
| 133 | * @param array $extensions (optional) |
||
| 134 | * An array of extensions that support the given language. |
||
| 135 | * @return array |
||
| 136 | * An array of Language information. |
||
| 137 | */ |
||
| 138 | private static function createLanguage($code, $name, $handle = null, array $extensions = array()) |
||
| 139 | { |
||
| 140 | return array( |
||
| 141 | $code => array( |
||
| 142 | 'name' => $name, |
||
| 143 | 'handle' => $handle, |
||
| 144 | 'extensions' => $extensions |
||
| 145 | ) |
||
| 146 | ); |
||
| 147 | } |
||
| 148 | |||
| 149 | /** |
||
| 150 | * Fetch all languages available in the core language folder and the language extensions. |
||
| 151 | * The function stores all language information in the private variable `$_languages`. |
||
| 152 | * It contains an array with the name and handle of each language and an array of all |
||
| 153 | * extensions available in that language. |
||
| 154 | * |
||
| 155 | * @throws UnexpectedValueException |
||
| 156 | * @throws RuntimeException |
||
| 157 | */ |
||
| 158 | private static function fetch() |
||
| 159 | { |
||
| 160 | if (!@is_readable(EXTENSIONS)) { |
||
| 161 | return; |
||
| 162 | } |
||
| 163 | |||
| 164 | // Fetch extensions |
||
| 165 | $extensions = new FilesystemIterator(EXTENSIONS); |
||
| 166 | |||
| 167 | // Language extensions |
||
| 168 | foreach ($extensions as $extension) { |
||
| 169 | if ($extension->isFile()) { |
||
| 170 | continue; |
||
| 171 | } |
||
| 172 | |||
| 173 | // Core translations |
||
| 174 | $core_handle = (strpos($extension->getFilename(), 'lang_') !== false) |
||
| 175 | ? str_replace('lang_', '', $extension->getFilename()) |
||
| 176 | : null; |
||
| 177 | |||
| 178 | // Loop over the `/lang` directory of this `$extension` searching for language |
||
| 179 | // files. If `/lang` isn't a directory, `UnexpectedValueException` will be |
||
| 180 | // thrown. |
||
| 181 | try { |
||
| 182 | $path = $extension->getPathname() . '/lang'; |
||
| 183 | if (!is_dir($path)) { |
||
| 184 | continue; |
||
| 185 | } |
||
| 186 | |||
| 187 | $directory = new GlobIterator($path . '/lang.*.php'); |
||
| 188 | foreach ($directory as $file) { |
||
| 189 | include($file->getPathname()); |
||
| 190 | |||
| 191 | // Get language code (chars between lang. and .php) |
||
| 192 | $code = substr($file->getFilename(), 5, -4); |
||
| 193 | $lang = null; |
||
| 194 | $handle = null; |
||
| 195 | $extensions = array(); |
||
| 196 | |||
| 197 | // Set lang, handle and extensions if defined. |
||
| 198 | if (isset(self::$_languages[$code])) { |
||
| 199 | $lang = self::$_languages[$code]; |
||
| 200 | $handle = $lang['handle']; |
||
| 201 | $extensions = $lang['extensions']; |
||
| 202 | } |
||
| 203 | |||
| 204 | // Core translations |
||
| 205 | if ($core_handle) { |
||
| 206 | $handle = $core_handle; |
||
| 207 | |||
| 208 | // Extension translations |
||
| 209 | } else { |
||
| 210 | $extensions = array_merge(array($extension->getFilename()), $extensions); |
||
| 211 | } |
||
| 212 | |||
| 213 | // Merge languages ($about is declared inside the included $file) |
||
| 214 | $temp = self::createLanguage($code, $about['name'], $handle, $extensions); |
||
| 215 | |||
| 216 | if (isset($lang)) { |
||
| 217 | foreach ($lang as $key => $value) { |
||
| 218 | // Prevent missing or nulled values overwriting existing values |
||
| 219 | // which can occur if a translation file is not correct. |
||
| 220 | if (!isset($temp[$code][$key]) || empty($temp[$code][$key])) { |
||
| 221 | continue; |
||
| 222 | } |
||
| 223 | |||
| 224 | self::$_languages[$code][$key] = $temp[$code][$key]; |
||
| 225 | } |
||
| 226 | } else { |
||
| 227 | self::$_languages[$code] = $temp[$code]; |
||
| 228 | } |
||
| 229 | } |
||
| 230 | } catch (Exception $ex) { |
||
| 231 | continue; |
||
| 232 | } |
||
| 233 | } |
||
| 234 | } |
||
| 235 | |||
| 236 | /** |
||
| 237 | * Set system language, load translations for core and extensions. If the specified language |
||
| 238 | * cannot be found, Symphony will default to English. |
||
| 239 | * |
||
| 240 | * Note: Beginning with Symphony 2.2 translations bundled with extensions will only be loaded |
||
| 241 | * when the core dictionary of the specific language is available. |
||
| 242 | * |
||
| 243 | * @param string $code |
||
| 244 | * Language code, e. g. 'en' or 'pt-br' |
||
| 245 | * @param boolean $checkStatus (optional) |
||
| 246 | * If false, set the language even if it's not enabled. Defaults to true. |
||
| 247 | */ |
||
| 248 | public static function set($code, $checkStatus = true) |
||
| 249 | { |
||
| 250 | if (!$code || $code == self::get()) { |
||
| 251 | return; |
||
| 252 | } |
||
| 253 | |||
| 254 | // Language file available |
||
| 255 | if ($code !== 'en' && (self::isLanguageEnabled($code) || $checkStatus === false)) { |
||
| 256 | // Store desired language code |
||
| 257 | self::$_lang = $code; |
||
| 258 | |||
| 259 | // Clear dictionary |
||
| 260 | self::$_dictionary = array(); |
||
| 261 | |||
| 262 | // Load core translations |
||
| 263 | self::load(vsprintf('%s/lang_%s/lang/lang.%s.php', array( |
||
| 264 | EXTENSIONS, self::$_languages[$code]['handle'], $code |
||
| 265 | ))); |
||
| 266 | |||
| 267 | // Load extension translations |
||
| 268 | if (is_array(self::$_languages[$code]['extensions'])) { |
||
| 269 | foreach (self::$_languages[$code]['extensions'] as $extension) { |
||
| 270 | self::load(vsprintf('%s/%s/lang/lang.%s.php', array( |
||
| 271 | EXTENSIONS, $extension, $code |
||
| 272 | ))); |
||
| 273 | } |
||
| 274 | } |
||
| 275 | |||
| 276 | // Language file unavailable, use default language |
||
| 277 | } else { |
||
| 278 | self::$_lang = 'en'; |
||
| 279 | |||
| 280 | // Log error, if possible |
||
| 281 | if ($code !== 'en' && class_exists('Symphony', false) && Symphony::Log() instanceof Log) { |
||
| 282 | Symphony::Log()->pushToLog( |
||
| 283 | __('The selected language, %s, could not be found. Using default English dictionary instead.', array($code)), |
||
| 284 | E_ERROR, |
||
| 285 | true |
||
| 286 | ); |
||
| 287 | } |
||
| 288 | } |
||
| 289 | } |
||
| 290 | |||
| 291 | /** |
||
| 292 | * Given a valid language code, this function checks if the language is enabled. |
||
| 293 | * |
||
| 294 | * @since Symphony 2.3 |
||
| 295 | * @param string $code |
||
| 296 | * Language code, e. g. 'en' or 'pt-br' |
||
| 297 | * @return boolean |
||
| 298 | * If true, the language is enabled. |
||
| 299 | */ |
||
| 300 | public static function isLanguageEnabled($code) |
||
| 301 | { |
||
| 302 | if ($code == 'en') { |
||
| 303 | return true; |
||
| 304 | } |
||
| 305 | |||
| 306 | $handle = (isset(self::$_languages[$code])) ? self::$_languages[$code]['handle'] : ''; |
||
| 307 | $enabled_extensions = array(); |
||
| 308 | |||
| 309 | // Fetch list of active extensions |
||
| 310 | if (class_exists('Symphony', false) && (!is_null(Symphony::ExtensionManager()))) { |
||
| 311 | $enabled_extensions = Symphony::ExtensionManager()->listInstalledHandles(); |
||
| 312 | } |
||
| 313 | |||
| 314 | return in_array('lang_' . $handle, $enabled_extensions); |
||
| 315 | } |
||
| 316 | |||
| 317 | /** |
||
| 318 | * Load language file. Each language file contains three arrays: |
||
| 319 | * about, dictionary and transliterations. |
||
| 320 | * |
||
| 321 | * @param string $path |
||
| 322 | * Path of the language file that should be loaded |
||
| 323 | */ |
||
| 324 | private static function load($path) |
||
| 325 | { |
||
| 326 | // Load language file |
||
| 327 | if (file_exists($path)) { |
||
| 328 | require($path); |
||
| 329 | } |
||
| 330 | |||
| 331 | // Populate dictionary ($dictionary is declared inside $path) |
||
| 332 | if (isset($dictionary) && is_array($dictionary)) { |
||
| 333 | self::$_dictionary = array_merge(self::$_dictionary, $dictionary); |
||
| 334 | } |
||
| 335 | |||
| 336 | // Populate transliterations ($transliterations is declared inside $path) |
||
| 337 | if (isset($transliterations) && is_array($transliterations)) { |
||
| 338 | self::$_transliterations = array_merge(self::$_transliterations, $transliterations); |
||
| 339 | } |
||
| 340 | } |
||
| 341 | |||
| 342 | /** |
||
| 343 | * Get current language |
||
| 344 | * |
||
| 345 | * @return string |
||
| 346 | */ |
||
| 347 | public static function get() |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * This function is an internal alias for `__()`. |
||
| 354 | * |
||
| 355 | * @since Symphony 2.3 |
||
| 356 | * @see toolkit.__() |
||
| 357 | * @param string $string |
||
| 358 | * The string that should be translated |
||
| 359 | * @param array $inserts (optional) |
||
| 360 | * Optional array used to replace translation placeholders, defaults to NULL |
||
| 361 | * @param string $namespace (optional) |
||
| 362 | * Optional string used to define the namespace, defaults to NULL. |
||
| 363 | * @return string |
||
| 364 | * Returns the translated string |
||
| 365 | */ |
||
| 366 | public static function translate($string, array $inserts = null, $namespace = null) |
||
| 367 | { |
||
| 368 | if (is_null($namespace) && class_exists('Symphony', false)) { |
||
| 369 | $namespace = Symphony::getPageNamespace(); |
||
| 370 | } |
||
| 371 | |||
| 372 | if (isset($namespace, self::$_dictionary[$namespace][$string])) { |
||
| 373 | $translated = self::$_dictionary[$namespace][$string]; |
||
| 374 | } elseif (isset(self::$_dictionary[$string])) { |
||
| 375 | $translated = self::$_dictionary[$string]; |
||
| 376 | } else { |
||
| 377 | $translated = $string; |
||
| 378 | } |
||
| 379 | |||
| 380 | $translated = empty($translated) ? $string : $translated; |
||
| 381 | |||
| 382 | // Replace translation placeholders |
||
| 383 | if (is_array($inserts) && !empty($inserts)) { |
||
| 384 | $translated = vsprintf($translated, $inserts); |
||
| 385 | } |
||
| 386 | |||
| 387 | return $translated; |
||
| 388 | } |
||
| 389 | |||
| 390 | /** |
||
| 391 | * Get an array of the codes and names of all languages that are available system wide. |
||
| 392 | * |
||
| 393 | * Note: Beginning with Symphony 2.2 language files are only available |
||
| 394 | * when the language extension is explicitly enabled. |
||
| 395 | * |
||
| 396 | * @param boolean $checkStatus (optional) |
||
| 397 | * If false, retrieves a list a languages that support core translation. |
||
| 398 | * @return array |
||
| 399 | * Returns an associative array of language codes and names, e. g. 'en' => 'English' |
||
| 400 | */ |
||
| 401 | public static function getAvailableLanguages($checkStatus = true) |
||
| 402 | { |
||
| 403 | $languages = array(); |
||
| 404 | |||
| 405 | // Get available languages |
||
| 406 | foreach (self::$_languages as $key => $language) { |
||
| 407 | if (self::isLanguageEnabled($key) || ($checkStatus === false && isset($language['handle']))) { |
||
| 408 | $languages[$key] = $language['name']; |
||
| 409 | } |
||
| 410 | } |
||
| 411 | |||
| 412 | // Return languages codes |
||
| 413 | return $languages; |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Check if Symphony is localised. |
||
| 418 | * |
||
| 419 | * @return boolean |
||
| 420 | * Returns true for localized system, false for English system |
||
| 421 | */ |
||
| 422 | public static function isLocalized() |
||
| 425 | } |
||
| 426 | |||
| 427 | /** |
||
| 428 | * Localize dates. |
||
| 429 | * |
||
| 430 | * @param string $string |
||
| 431 | * Standard date that should be localized |
||
| 432 | * @return string |
||
| 433 | * Return the given date with translated month and day names |
||
| 434 | */ |
||
| 435 | public static function localizeDate($string) |
||
| 436 | { |
||
| 437 | // Only translate dates in localized environments |
||
| 438 | if (self::isLocalized()) { |
||
| 439 | foreach (self::$_datetime_dictionary as $value) { |
||
| 440 | $string = preg_replace('/\b' . $value . '\b/i', self::translate($value), $string); |
||
| 441 | } |
||
| 442 | } |
||
| 443 | |||
| 444 | return $string; |
||
| 445 | } |
||
| 446 | |||
| 447 | /** |
||
| 448 | * Standardize dates. |
||
| 449 | * |
||
| 450 | * @param string $string |
||
| 451 | * Localized date that should be standardized |
||
| 452 | * @return string |
||
| 453 | * Returns the given date with English month and day names |
||
| 454 | */ |
||
| 455 | public static function standardizeDate($string) |
||
| 456 | { |
||
| 457 | // Only standardize dates in localized environments |
||
| 458 | if (self::isLocalized()) { |
||
| 459 | |||
| 460 | // Translate names to English |
||
| 461 | foreach (self::$_datetime_dictionary as $values) { |
||
| 462 | $string = preg_replace('/\b' . self::translate($values) . '\b/i' . (self::isUnicodeCompiled() === true ? 'u' : null), $values, $string); |
||
| 463 | } |
||
| 464 | |||
| 465 | // Replace custom date and time separator with space: |
||
| 466 | // This is important, otherwise the `DateTime` constructor may break |
||
| 467 | // @todo Test if this separator is still required. It's a hidden setting |
||
| 468 | // and users are only aware of it if they go digging/pointed in the right direction |
||
| 469 | $separator = Symphony::Configuration()->get('datetime_separator', 'region'); |
||
| 470 | if ($separator !== ' ') { |
||
| 471 | $string = str_replace($separator, ' ', $string); |
||
| 472 | } |
||
| 473 | } |
||
| 474 | |||
| 475 | return $string; |
||
| 476 | } |
||
| 477 | |||
| 478 | /** |
||
| 479 | * Given a string, this will clean it for use as a Symphony handle. Preserves multi-byte characters. |
||
| 480 | * |
||
| 481 | * @param string $string |
||
| 482 | * String to be cleaned up |
||
| 483 | * @param integer $max_length |
||
| 484 | * The maximum number of characters in the handle |
||
| 485 | * @param string $delim |
||
| 486 | * All non-valid characters will be replaced with this |
||
| 487 | * @param boolean $uriencode |
||
| 488 | * Force the resultant string to be uri encoded making it safe for URLs |
||
| 489 | * @param boolean $apply_transliteration |
||
| 490 | * If true, this will run the string through an array of substitution characters |
||
| 491 | * @param array $additional_rule_set |
||
| 492 | * An array of REGEX patterns that should be applied to the `$string`. This |
||
| 493 | * occurs after the string has been trimmed and joined with the `$delim` |
||
| 494 | * @return string |
||
| 495 | * Returns resultant handle |
||
| 496 | */ |
||
| 497 | public static function createHandle($string, $max_length = 255, $delim = '-', $uriencode = false, $apply_transliteration = true, $additional_rule_set = null) |
||
| 498 | { |
||
| 499 | // Use the transliteration table if provided |
||
| 500 | if ($apply_transliteration === true) { |
||
| 501 | $string = self::applyTransliterations($string); |
||
| 502 | } |
||
| 503 | |||
| 504 | return General::createHandle($string, $max_length, $delim, $uriencode, $additional_rule_set); |
||
| 505 | } |
||
| 506 | |||
| 507 | /** |
||
| 508 | * Given a string, this will clean it for use as a filename. Preserves multi-byte characters. |
||
| 509 | * |
||
| 510 | * @param string $string |
||
| 511 | * String to be cleaned up |
||
| 512 | * @param string $delim |
||
| 513 | * Replacement for invalid characters |
||
| 514 | * @param boolean $apply_transliteration |
||
| 515 | * If true, umlauts and special characters will be substituted |
||
| 516 | * @return string |
||
| 517 | * Returns created filename |
||
| 518 | */ |
||
| 519 | public static function createFilename($string, $delim='-', $apply_transliteration = true) |
||
| 520 | { |
||
| 521 | // Use the transliteration table if provided |
||
| 522 | if ($apply_transliteration === true) { |
||
| 523 | $file = pathinfo($string); |
||
| 524 | $string = self::applyTransliterations($file['filename']) . '.' . $file['extension']; |
||
| 525 | } |
||
| 526 | |||
| 527 | return General::createFilename($string, $delim); |
||
| 528 | } |
||
| 529 | |||
| 530 | /** |
||
| 531 | * This function replaces special characters according to the values stored inside |
||
| 532 | * `$_transliterations`. |
||
| 533 | * |
||
| 534 | * @since Symphony 2.3 |
||
| 535 | * @param string $string |
||
| 536 | * The string that should be cleaned-up |
||
| 537 | * @return mixed |
||
| 538 | * Returns the transliterated string |
||
| 539 | */ |
||
| 540 | private static function applyTransliterations($string) |
||
| 541 | { |
||
| 542 | // Apply the straight transliterations with strtr as it's much faster |
||
| 543 | $string = strtr($string, self::$_transliterations['straight']); |
||
| 544 | |||
| 545 | // Apply the regex rules over the resulting $string |
||
| 546 | return preg_replace( |
||
| 547 | array_keys(self::$_transliterations['regexp']), |
||
| 548 | array_values(self::$_transliterations['regexp']), |
||
| 549 | $string |
||
| 550 | ); |
||
| 551 | } |
||
| 552 | |||
| 553 | /** |
||
| 554 | * Returns boolean if PHP has been compiled with unicode support. This is |
||
| 555 | * useful to determine if unicode modifier's can be used in regular expression's |
||
| 556 | * |
||
| 557 | * @link http://stackoverflow.com/questions/4509576/detect-if-pcre-was-built-without-the-enable-unicode-properties-or-enable-utf8 |
||
| 558 | * @since Symphony 2.2.2 |
||
| 559 | * @return boolean |
||
| 560 | */ |
||
| 561 | public static function isUnicodeCompiled() |
||
| 564 | } |
||
| 565 | } |
||
| 566 |