[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Language; 11 12 use Joomla\CMS\Cache\CacheControllerFactoryInterface; 13 use Joomla\CMS\Cache\Controller\OutputController; 14 use Joomla\CMS\Factory; 15 use Joomla\CMS\Filesystem\File; 16 use Joomla\CMS\Installer\Installer; 17 use Joomla\CMS\Log\Log; 18 use Joomla\Registry\Registry; 19 use Joomla\Utilities\ArrayHelper; 20 21 // phpcs:disable PSR1.Files.SideEffects 22 \defined('JPATH_PLATFORM') or die; 23 // phpcs:enable PSR1.Files.SideEffects 24 25 /** 26 * Language helper class 27 * 28 * @since 1.5 29 */ 30 class LanguageHelper 31 { 32 /** 33 * Builds a list of the system languages which can be used in a select option 34 * 35 * @param string $actualLanguage Client key for the area 36 * @param string $basePath Base path to use 37 * @param boolean $caching True if caching is used 38 * @param boolean $installed Get only installed languages 39 * 40 * @return array List of system languages 41 * 42 * @since 1.5 43 */ 44 public static function createLanguageList($actualLanguage, $basePath = JPATH_BASE, $caching = false, $installed = false) 45 { 46 $list = array(); 47 $clientId = $basePath === JPATH_ADMINISTRATOR ? 1 : 0; 48 $languages = $installed ? static::getInstalledLanguages($clientId, true) : self::getKnownLanguages($basePath); 49 50 foreach ($languages as $languageCode => $language) { 51 $metadata = $installed ? $language->metadata : $language; 52 53 $list[] = array( 54 'text' => $metadata['nativeName'] ?? $metadata['name'], 55 'value' => $languageCode, 56 'selected' => $languageCode === $actualLanguage ? 'selected="selected"' : null, 57 ); 58 } 59 60 return $list; 61 } 62 63 /** 64 * Tries to detect the language. 65 * 66 * @return string locale or null if not found 67 * 68 * @since 1.5 69 */ 70 public static function detectLanguage() 71 { 72 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 73 $browserLangs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); 74 $systemLangs = self::getLanguages(); 75 76 foreach ($browserLangs as $browserLang) { 77 // Slice out the part before ; on first step, the part before - on second, place into array 78 $browserLang = substr($browserLang, 0, strcspn($browserLang, ';')); 79 $primary_browserLang = substr($browserLang, 0, 2); 80 81 foreach ($systemLangs as $systemLang) { 82 // Take off 3 letters iso code languages as they can't match browsers' languages and default them to en 83 $Jinstall_lang = $systemLang->lang_code; 84 85 if (\strlen($Jinstall_lang) < 6) { 86 if (strtolower($browserLang) == strtolower(substr($systemLang->lang_code, 0, \strlen($browserLang)))) { 87 return $systemLang->lang_code; 88 } elseif ($primary_browserLang == substr($systemLang->lang_code, 0, 2)) { 89 $primaryDetectedLang = $systemLang->lang_code; 90 } 91 } 92 } 93 94 if (isset($primaryDetectedLang)) { 95 return $primaryDetectedLang; 96 } 97 } 98 } 99 } 100 101 /** 102 * Get available languages 103 * 104 * @param string $key Array key 105 * 106 * @return array An array of published languages 107 * 108 * @since 1.6 109 */ 110 public static function getLanguages($key = 'default') 111 { 112 static $languages; 113 114 if (empty($languages)) { 115 // Installation uses available languages 116 if (Factory::getApplication()->isClient('installation')) { 117 $languages[$key] = array(); 118 $knownLangs = self::getKnownLanguages(JPATH_BASE); 119 120 foreach ($knownLangs as $metadata) { 121 // Take off 3 letters iso code languages as they can't match browsers' languages and default them to en 122 $obj = new \stdClass(); 123 $obj->lang_code = $metadata['tag']; 124 $languages[$key][] = $obj; 125 } 126 } else { 127 /** @var OutputController $cache */ 128 $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) 129 ->createCacheController('output', ['defaultgroup' => 'com_languages']); 130 131 if ($cache->contains('languages')) { 132 $languages = $cache->get('languages'); 133 } else { 134 $db = Factory::getDbo(); 135 $query = $db->getQuery(true) 136 ->select('*') 137 ->from($db->quoteName('#__languages')) 138 ->where($db->quoteName('published') . ' = 1') 139 ->order($db->quoteName('ordering') . ' ASC'); 140 $db->setQuery($query); 141 142 $languages['default'] = $db->loadObjectList(); 143 $languages['sef'] = array(); 144 $languages['lang_code'] = array(); 145 146 if (isset($languages['default'][0])) { 147 foreach ($languages['default'] as $lang) { 148 $languages['sef'][$lang->sef] = $lang; 149 $languages['lang_code'][$lang->lang_code] = $lang; 150 } 151 } 152 153 $cache->store($languages, 'languages'); 154 } 155 } 156 } 157 158 return $languages[$key]; 159 } 160 161 /** 162 * Get a list of installed languages. 163 * 164 * @param integer $clientId The client app id. 165 * @param boolean $processMetaData Fetch Language metadata. 166 * @param boolean $processManifest Fetch Language manifest. 167 * @param string $pivot The pivot of the returning array. 168 * @param string $orderField Field to order the results. 169 * @param string $orderDirection Direction to order the results. 170 * 171 * @return array Array with the installed languages. 172 * 173 * @since 3.7.0 174 */ 175 public static function getInstalledLanguages( 176 $clientId = null, 177 $processMetaData = false, 178 $processManifest = false, 179 $pivot = 'element', 180 $orderField = null, 181 $orderDirection = null 182 ) { 183 static $installedLanguages = null; 184 185 if ($installedLanguages === null) { 186 /** @var OutputController $cache */ 187 $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) 188 ->createCacheController('output', ['defaultgroup' => 'com_languages']); 189 190 if ($cache->contains('installedlanguages')) { 191 $installedLanguages = $cache->get('installedlanguages'); 192 } else { 193 $db = Factory::getDbo(); 194 195 $query = $db->getQuery(true) 196 ->select( 197 [ 198 $db->quoteName('element'), 199 $db->quoteName('name'), 200 $db->quoteName('client_id'), 201 $db->quoteName('extension_id'), 202 ] 203 ) 204 ->from($db->quoteName('#__extensions')) 205 ->where( 206 [ 207 $db->quoteName('type') . ' = ' . $db->quote('language'), 208 $db->quoteName('state') . ' = 0', 209 $db->quoteName('enabled') . ' = 1', 210 ] 211 ); 212 213 $installedLanguages = $db->setQuery($query)->loadObjectList(); 214 215 $cache->store($installedLanguages, 'installedlanguages'); 216 } 217 } 218 219 $clients = $clientId === null ? array(0, 1) : array((int) $clientId); 220 $languages = array( 221 0 => array(), 222 1 => array(), 223 ); 224 225 foreach ($installedLanguages as $language) { 226 // If the language client is not needed continue cycle. Drop for performance. 227 if (!\in_array((int) $language->client_id, $clients)) { 228 continue; 229 } 230 231 $lang = $language; 232 233 if ($processMetaData || $processManifest) { 234 $clientPath = (int) $language->client_id === 0 ? JPATH_SITE : JPATH_ADMINISTRATOR; 235 $metafile = self::getLanguagePath($clientPath, $language->element) . '/langmetadata.xml'; 236 237 if (!is_file($metafile)) { 238 $metafile = self::getLanguagePath($clientPath, $language->element) . '/' . $language->element . '.xml'; 239 } 240 241 // Process the language metadata. 242 if ($processMetaData) { 243 try { 244 $lang->metadata = self::parseXMLLanguageFile($metafile); 245 } catch (\Exception $e) { 246 // Not able to process xml language file. Fail silently. 247 Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METAFILE', $language->element, $metafile), Log::WARNING, 'language'); 248 249 continue; 250 } 251 252 // No metadata found, not a valid language. Fail silently. 253 if (!\is_array($lang->metadata)) { 254 Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METADATA', $language->element, $metafile), Log::WARNING, 'language'); 255 256 continue; 257 } 258 } 259 260 // Process the language manifest. 261 if ($processManifest) { 262 try { 263 $lang->manifest = Installer::parseXMLInstallFile($metafile); 264 } catch (\Exception $e) { 265 // Not able to process xml language file. Fail silently. 266 Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METAFILE', $language->element, $metafile), Log::WARNING, 'language'); 267 268 continue; 269 } 270 271 // No metadata found, not a valid language. Fail silently. 272 if (!\is_array($lang->manifest)) { 273 Log::add(Text::sprintf('JLIB_LANGUAGE_ERROR_CANNOT_LOAD_METADATA', $language->element, $metafile), Log::WARNING, 'language'); 274 275 continue; 276 } 277 } 278 } 279 280 $languages[$language->client_id][] = $lang; 281 } 282 283 // Order the list, if needed. 284 if ($orderField !== null && $orderDirection !== null) { 285 $orderDirection = strtolower($orderDirection) === 'desc' ? -1 : 1; 286 287 foreach ($languages as $cId => $language) { 288 // If the language client is not needed continue cycle. Drop for performance. 289 if (!\in_array($cId, $clients)) { 290 continue; 291 } 292 293 $languages[$cId] = ArrayHelper::sortObjects($languages[$cId], $orderField, $orderDirection, true, true); 294 } 295 } 296 297 // Add the pivot, if needed. 298 if ($pivot !== null) { 299 foreach ($languages as $cId => $language) { 300 // If the language client is not needed continue cycle. Drop for performance. 301 if (!\in_array($cId, $clients)) { 302 continue; 303 } 304 305 $languages[$cId] = ArrayHelper::pivot($languages[$cId], $pivot); 306 } 307 } 308 309 return $clientId !== null ? $languages[$clientId] : $languages; 310 } 311 312 /** 313 * Get a list of content languages. 314 * 315 * @param array $publishedStates Array with the content language published states. Empty array for all. 316 * @param boolean $checkInstalled Check if the content language is installed. 317 * @param string $pivot The pivot of the returning array. 318 * @param string $orderField Field to order the results. 319 * @param string $orderDirection Direction to order the results. 320 * 321 * @return array Array of the content languages. 322 * 323 * @since 3.7.0 324 */ 325 public static function getContentLanguages( 326 $publishedStates = array(1), 327 $checkInstalled = true, 328 $pivot = 'lang_code', 329 $orderField = null, 330 $orderDirection = null 331 ) { 332 static $contentLanguages = null; 333 334 if ($contentLanguages === null) { 335 /** @var OutputController $cache */ 336 $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class) 337 ->createCacheController('output', ['defaultgroup' => 'com_languages']); 338 339 if ($cache->contains('contentlanguages')) { 340 $contentLanguages = $cache->get('contentlanguages'); 341 } else { 342 $db = Factory::getDbo(); 343 344 $query = $db->getQuery(true) 345 ->select('*') 346 ->from($db->quoteName('#__languages')); 347 348 $contentLanguages = $db->setQuery($query)->loadObjectList(); 349 350 $cache->store($contentLanguages, 'contentlanguages'); 351 } 352 } 353 354 $languages = $contentLanguages; 355 356 // B/C layer. Before 3.8.3. 357 if ($publishedStates === true) { 358 $publishedStates = array(1); 359 } elseif ($publishedStates === false) { 360 $publishedStates = array(); 361 } 362 363 // Check the language published state, if needed. 364 if (\count($publishedStates) > 0) { 365 foreach ($languages as $key => $language) { 366 if (!\in_array((int) $language->published, $publishedStates, true)) { 367 unset($languages[$key]); 368 } 369 } 370 } 371 372 // Check if the language is installed, if needed. 373 if ($checkInstalled) { 374 $languages = array_values(array_intersect_key(ArrayHelper::pivot($languages, 'lang_code'), static::getInstalledLanguages(0))); 375 } 376 377 // Order the list, if needed. 378 if ($orderField !== null && $orderDirection !== null) { 379 $languages = ArrayHelper::sortObjects($languages, $orderField, strtolower($orderDirection) === 'desc' ? -1 : 1, true, true); 380 } 381 382 // Add the pivot, if needed. 383 if ($pivot !== null) { 384 $languages = ArrayHelper::pivot($languages, $pivot); 385 } 386 387 return $languages; 388 } 389 390 /** 391 * Parse strings from a language file. 392 * 393 * @param string $fileName The language ini file path. 394 * @param boolean $debug If set to true debug language ini file. 395 * 396 * @return array The strings parsed. 397 * 398 * @since 3.9.0 399 */ 400 public static function parseIniFile($fileName, $debug = false) 401 { 402 // Check if file exists. 403 if (!is_file($fileName)) { 404 return array(); 405 } 406 407 // Capture hidden PHP errors from the parsing. 408 if ($debug === true) { 409 // See https://www.php.net/manual/en/reserved.variables.phperrormsg.php 410 $php_errormsg = null; 411 412 $trackErrors = ini_get('track_errors'); 413 ini_set('track_errors', true); 414 } 415 416 // This was required for https://github.com/joomla/joomla-cms/issues/17198 but not sure what server setup 417 // issue it is solving 418 $disabledFunctions = explode(',', ini_get('disable_functions')); 419 $isParseIniFileDisabled = \in_array('parse_ini_file', array_map('trim', $disabledFunctions)); 420 421 if (!\function_exists('parse_ini_file') || $isParseIniFileDisabled) { 422 $contents = file_get_contents($fileName); 423 $strings = @parse_ini_string($contents); 424 } else { 425 $strings = @parse_ini_file($fileName); 426 } 427 428 // Restore error tracking to what it was before. 429 if ($debug === true) { 430 ini_set('track_errors', $trackErrors); 431 } 432 433 return \is_array($strings) ? $strings : array(); 434 } 435 436 /** 437 * Save strings to a language file. 438 * 439 * @param string $fileName The language ini file path. 440 * @param array $strings The array of strings. 441 * 442 * @return boolean True if saved, false otherwise. 443 * 444 * @since 3.7.0 445 */ 446 public static function saveToIniFile($fileName, array $strings) 447 { 448 // Escape double quotes. 449 foreach ($strings as $key => $string) { 450 $strings[$key] = addcslashes($string, '"'); 451 } 452 453 // Write override.ini file with the strings. 454 $registry = new Registry($strings); 455 456 return File::write($fileName, $registry->toString('INI')); 457 } 458 459 /** 460 * Checks if a language exists. 461 * 462 * This is a simple, quick check for the directory that should contain language files for the given user. 463 * 464 * @param string $lang Language to check. 465 * @param string $basePath Optional path to check. 466 * 467 * @return boolean True if the language exists. 468 * 469 * @since 3.7.0 470 */ 471 public static function exists($lang, $basePath = JPATH_BASE) 472 { 473 static $paths = array(); 474 475 // Return false if no language was specified 476 if (!$lang) { 477 return false; 478 } 479 480 $path = $basePath . '/language/' . $lang; 481 482 // Return previous check results if it exists 483 if (isset($paths[$path])) { 484 return $paths[$path]; 485 } 486 487 // Check if the language exists 488 $paths[$path] = is_dir($path); 489 490 return $paths[$path]; 491 } 492 493 /** 494 * Returns an associative array holding the metadata. 495 * 496 * @param string $lang The name of the language. 497 * 498 * @return mixed If $lang exists return key/value pair with the language metadata, otherwise return NULL. 499 * 500 * @since 3.7.0 501 */ 502 public static function getMetadata($lang) 503 { 504 $file = self::getLanguagePath(JPATH_BASE, $lang) . '/langmetadata.xml'; 505 506 if (!is_file($file)) { 507 $file = self::getLanguagePath(JPATH_BASE, $lang) . '/' . $lang . '.xml'; 508 } 509 510 $result = null; 511 512 if (is_file($file)) { 513 $result = self::parseXMLLanguageFile($file); 514 } 515 516 if (empty($result)) { 517 return; 518 } 519 520 return $result; 521 } 522 523 /** 524 * Returns a list of known languages for an area 525 * 526 * @param string $basePath The basepath to use 527 * 528 * @return array key/value pair with the language file and real name. 529 * 530 * @since 3.7.0 531 */ 532 public static function getKnownLanguages($basePath = JPATH_BASE) 533 { 534 return self::parseLanguageFiles(self::getLanguagePath($basePath)); 535 } 536 537 /** 538 * Get the path to a language 539 * 540 * @param string $basePath The basepath to use. 541 * @param string $language The language tag. 542 * 543 * @return string language related path or null. 544 * 545 * @since 3.7.0 546 */ 547 public static function getLanguagePath($basePath = JPATH_BASE, $language = null) 548 { 549 return $basePath . '/language' . (!empty($language) ? '/' . $language : ''); 550 } 551 552 /** 553 * Searches for language directories within a certain base dir. 554 * 555 * @param string $dir directory of files. 556 * 557 * @return array Array holding the found languages as filename => real name pairs. 558 * 559 * @since 3.7.0 560 */ 561 public static function parseLanguageFiles($dir = null) 562 { 563 $languages = array(); 564 565 // Search main language directory for subdirectories 566 foreach (glob($dir . '/*', GLOB_NOSORT | GLOB_ONLYDIR) as $directory) { 567 // But only directories with lang code format 568 if (preg_match('#/[a-z]{2,3}-[A-Z]{2}$#', $directory)) { 569 $dirPathParts = pathinfo($directory); 570 $file = $directory . '/langmetadata.xml'; 571 572 if (!is_file($file)) { 573 $file = $directory . '/' . $dirPathParts['filename'] . '.xml'; 574 } 575 576 if (!is_file($file)) { 577 continue; 578 } 579 580 try { 581 // Get installed language metadata from xml file and merge it with lang array 582 if ($metadata = self::parseXMLLanguageFile($file)) { 583 $languages = array_replace($languages, array($dirPathParts['filename'] => $metadata)); 584 } 585 } catch (\RuntimeException $e) { 586 // Ignore it 587 } 588 } 589 } 590 591 return $languages; 592 } 593 594 /** 595 * Parse XML file for language information. 596 * 597 * @param string $path Path to the XML files. 598 * 599 * @return array Array holding the found metadata as a key => value pair. 600 * 601 * @since 3.7.0 602 * @throws \RuntimeException 603 */ 604 public static function parseXMLLanguageFile($path) 605 { 606 if (!is_readable($path)) { 607 throw new \RuntimeException('File not found or not readable'); 608 } 609 610 // Try to load the file 611 $xml = simplexml_load_file($path); 612 613 if (!$xml) { 614 return; 615 } 616 617 // Check that it's a metadata file 618 if ((string) $xml->getName() !== 'metafile') { 619 return; 620 } 621 622 $metadata = array(); 623 624 foreach ($xml->metadata->children() as $child) { 625 $metadata[$child->getName()] = (string) $child; 626 } 627 628 return $metadata; 629 } 630 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |