Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/vendors/Textpattern/L10n/Lang.php - 823 lines - 25920 bytes - Summary - Text - Print

Description: Language manipulation.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * https://textpattern.com/
   6   *
   7   * Copyright (C) 2020 The Textpattern Development Team
   8   *
   9   * This file is part of Textpattern.
  10   *
  11   * Textpattern is free software; you can redistribute it and/or
  12   * modify it under the terms of the GNU General Public License
  13   * as published by the Free Software Foundation, version 2.
  14   *
  15   * Textpattern is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License
  21   * along with Textpattern. If not, see <https://www.gnu.org/licenses/>.
  22   */
  23  
  24  /**
  25   * Language manipulation.
  26   *
  27   * @since   4.7.0
  28   * @package L10n
  29   */
  30  
  31  namespace Textpattern\L10n;
  32  
  33  class Lang implements \Textpattern\Container\ReusableInterface
  34  {
  35      /**
  36       * Language base directory that houses all the language files/textpacks.
  37       *
  38       * @var string
  39       */
  40  
  41      protected $langDirectory = null;
  42  
  43      /**
  44       * List of files in the $langDirectory.
  45       *
  46       * @var array
  47       */
  48  
  49      protected $files = array();
  50  
  51      /**
  52       * The currently active language designator.
  53       *
  54       * @var string
  55       */
  56  
  57      protected $activeLang = null;
  58  
  59      /**
  60       * Metadata for languages installed in the database.
  61       *
  62       * @var array
  63       */
  64  
  65      protected $dbLangs = array();
  66  
  67      /**
  68       * Metadata for all available languages in the filesystem.
  69       *
  70       * @var array
  71       */
  72  
  73      protected $allLangs = array();
  74  
  75      /**
  76       * List of strings that have been loaded.
  77       *
  78       * @var array
  79       */
  80  
  81      protected $strings = null;
  82  
  83      /**
  84       * List of cached strings that have been temporarily overridden.
  85       *
  86       * @var array
  87       */
  88  
  89      protected $cachedStrings = null;
  90  
  91      /**
  92       * Array of events that have been loaded.
  93       *
  94       * @var array
  95       */
  96  
  97      protected $loaded = array();
  98  
  99      /**
 100       * Date format to use for the lastmod column.
 101       *
 102       * @var string
 103       */
 104  
 105      protected $lastmodFormat = 'YmdHis';
 106  
 107      /**
 108       * Constructor.
 109       *
 110       * @param string $langDirectory Language directory to use
 111       */
 112  
 113      public function __construct($langDirectory = null)
 114      {
 115          if ($langDirectory === null) {
 116              $langDirectory = txpath.DS.'lang'.DS;
 117          }
 118  
 119          $this->langDirectory = $langDirectory;
 120  
 121          if (!$this->files) {
 122              $this->files = $this->files();
 123          }
 124      }
 125  
 126      /**
 127       * Return all installed languages in the database.
 128       *
 129       * @return array Available language codes
 130       */
 131  
 132      public function installed()
 133      {
 134          if (!$this->dbLangs) {
 135              $this->available();
 136          }
 137  
 138          $installed_langs = array();
 139  
 140          foreach ($this->dbLangs as $row) {
 141              $installed_langs[] = $row['lang'];
 142          }
 143  
 144          return $installed_langs;
 145      }
 146  
 147      /**
 148       * Return all language files in the lang directory.
 149       *
 150       * @param  array $extensions Language files extensions
 151       * @return array Available language filenames
 152       */
 153  
 154      public function files($extensions = array('ini', 'textpack', 'txt'))
 155      {
 156          if (!is_dir($this->langDirectory) || !is_readable($this->langDirectory)) {
 157              trigger_error('Lang directory is not accessible: '.$this->langDirectory, E_USER_WARNING);
 158  
 159              return array();
 160          }
 161  
 162          if (defined('GLOB_BRACE')) {
 163              return glob($this->langDirectory.'*.{'.implode(',', $extensions).'}', GLOB_BRACE);
 164          }
 165  
 166          $files = array();
 167  
 168          foreach ((array)$extensions as $ext) {
 169              $files = array_merge($files, (array) glob($this->langDirectory.'*.'.$ext));
 170          }
 171  
 172          return $files;
 173      }
 174  
 175      /**
 176       * Locate a file in the lang directory based on a language code.
 177       *
 178       * @param  string $lang_code The language code to look up
 179       * @return string|null       The matching filename
 180       */
 181  
 182      public function findFilename($lang_code)
 183      {
 184          $out = null;
 185  
 186          if (!empty($this->files)) {
 187              foreach ($this->files as $file) {
 188                  $pathinfo = pathinfo($file);
 189  
 190                  if ($pathinfo['filename'] === $lang_code) {
 191                      $out = $file;
 192                      break;
 193                  }
 194              }
 195          }
 196  
 197          return $out;
 198      }
 199  
 200      /**
 201       * Read the meta info from the top of the given language file.
 202       *
 203       * @param  string $file The filename to read
 204       * @return array        Meta info such as language name, language code, language direction and last modified time
 205       */
 206  
 207      public function fetchMeta($file)
 208      {
 209          $meta = array();
 210  
 211          if (is_file($file) && is_readable($file)) {
 212              $numMetaRows = 4;
 213              $separator = '=>';
 214              extract(pathinfo($file));
 215              $filename = preg_replace('/\.(txt|textpack|ini)$/i', '', $basename);
 216              $ini = strtolower($extension) == 'ini';
 217  
 218              $meta['filename'] = $filename;
 219  
 220              if ($fp = @fopen($file, 'r')) {
 221                  for ($idx = 0; $idx < $numMetaRows; $idx++) {
 222                      $rows[] = fgets($fp, 1024);
 223                  }
 224  
 225                  fclose($fp);
 226                  $meta['time'] = filemtime($file);
 227  
 228                  if ($ini) {
 229                      $langInfo = parse_ini_string(join($rows));
 230                      $meta['name'] = (!empty($langInfo['lang_name'])) ? $langInfo['lang_name'] : $filename;
 231                      $meta['code'] = (!empty($langInfo['lang_code'])) ? strtolower($langInfo['lang_code']) : $filename;
 232                      $meta['direction'] = (!empty($langInfo['lang_dir'])) ? strtolower($langInfo['lang_dir']) : 'ltr';
 233                  } else {
 234                      $langName = do_list($rows[1], $separator);
 235                      $langCode = do_list($rows[2], $separator);
 236                      $langDirection = do_list($rows[3], $separator);
 237  
 238                      $meta['name'] = (isset($langName[1])) ? $langName[1] : $filename;
 239                      $meta['code'] = (isset($langCode[1])) ? strtolower($langCode[1]) : $filename;
 240                      $meta['direction'] = (isset($langDirection[1])) ? strtolower($langDirection[1]) : 'ltr';
 241                  }
 242              }
 243          }
 244  
 245          return $meta;
 246      }
 247  
 248      /**
 249       * Fetch available languages.
 250       *
 251       * Depending on the flags, the returned array can contain active,
 252       * installed or available language metadata.
 253       *
 254       * @param  int $flags Determine which type of information to return
 255       * @param  int $force Force update the given information, even if it's already populated
 256       * @return array
 257       */
 258  
 259      public function available($flags = TEXTPATTERN_LANG_AVAILABLE, $force = 0)
 260      {
 261          if ($force & TEXTPATTERN_LANG_ACTIVE || $this->activeLang === null) {
 262              $this->activeLang = get_pref('language', TEXTPATTERN_DEFAULT_LANG, true);
 263              $this->activeLang = \Txp::get('\Textpattern\L10n\Locale')->validLocale($this->activeLang);
 264          }
 265  
 266          if ($force & TEXTPATTERN_LANG_INSTALLED || !$this->dbLangs) {
 267              // Need a value here for the language itself, not for each one of the rows.
 268              $ownClause = ($this->hasOwnerSupport() ? "owner = ''" : "1")." GROUP BY lang ORDER BY lastmod DESC";
 269              $this->dbLangs = safe_rows(
 270                  "lang, UNIX_TIMESTAMP(MAX(lastmod)) AS lastmod",
 271                  'txp_lang',
 272                  $ownClause
 273              );
 274          }
 275  
 276          if ($force & TEXTPATTERN_LANG_AVAILABLE || !$this->allLangs) {
 277              $currently_lang = array();
 278              $installed_lang = array();
 279              $available_lang = array();
 280  
 281              // Set up the current and installed array. Define their names as
 282              // 'unknown' for now in case the file is missing or mangled. The
 283              // name will be overwritten when reading from the filesystem if
 284              // it's intact.
 285              foreach ($this->dbLangs as $language) {
 286                  if ($language['lang'] === $this->activeLang) {
 287                      $currently_lang[$language['lang']] = array(
 288                          'db_lastmod' => $language['lastmod'],
 289                          'type'       => 'active',
 290                          'name'       => gTxt('unknown'),
 291                      );
 292                  } else {
 293                      $installed_lang[$language['lang']] = array(
 294                          'db_lastmod' => $language['lastmod'],
 295                          'type'       => 'installed',
 296                          'name'       => gTxt('unknown'),
 297                      );
 298                  }
 299              }
 300  
 301              // Get items from filesystem.
 302              if (!empty($this->files)) {
 303                  foreach ($this->files as $file) {
 304                      $meta = $this->fetchMeta($file);
 305  
 306                      if ($meta && !isset($available_lang[$meta['filename']])) {
 307                          $name = $meta['filename'];
 308  
 309                          if (array_key_exists($name, $currently_lang)) {
 310                              $currently_lang[$name]['name'] = $meta['name'];
 311                              $currently_lang[$name]['direction'] = $meta['direction'];
 312                              $currently_lang[$name]['file_lastmod'] = $meta['time'];
 313                          } elseif (array_key_exists($name, $installed_lang)) {
 314                              $installed_lang[$name]['name'] = $meta['name'];
 315                              $installed_lang[$name]['direction'] = $meta['direction'];
 316                              $installed_lang[$name]['file_lastmod'] = $meta['time'];
 317                          }
 318  
 319                          $available_lang[$name]['file_lastmod'] = $meta['time'];
 320                          $available_lang[$name]['name'] = $meta['name'];
 321                          $available_lang[$name]['direction'] = $meta['direction'];
 322                          $available_lang[$name]['type'] = 'available';
 323                      }
 324                  }
 325              }
 326  
 327              $this->allLangs = array(
 328                  'active'    => $currently_lang,
 329                  'installed' => $installed_lang,
 330                  'available' => $available_lang,
 331              );
 332          }
 333  
 334          $out = array();
 335  
 336          if ($flags & TEXTPATTERN_LANG_ACTIVE) {
 337              $out = array_merge($out, $this->allLangs['active']);
 338          }
 339  
 340          if ($flags & TEXTPATTERN_LANG_INSTALLED) {
 341              $out = array_merge($out, $this->allLangs['installed']);
 342          }
 343  
 344          if ($flags & TEXTPATTERN_LANG_AVAILABLE) {
 345              $out = array_merge($out, $this->allLangs['available']);
 346          }
 347  
 348          return $out;
 349      }
 350  
 351      /**
 352       * Set/overwrite the language strings. Chainable.
 353       *
 354       * @param array $strings Set of strings to use
 355       * @param bool  $merge   Whether to merge the strings (true) or replace them entirely (false)
 356       */
 357  
 358      public function setPack(array $strings, $merge = false)
 359      {
 360          if ((bool)$merge && is_array($this->strings)) {
 361              foreach ((array)$strings as $k => $v) {
 362                  $this->strings[$k] = $v;
 363              }
 364          } else {
 365              $this->strings = (array)$strings;
 366          }
 367  
 368          return $this;
 369      }
 370  
 371      /**
 372       * Fetch Textpack strings from the file matching the given $lang_code.
 373       *
 374       * A subset of the strings may be fetched by supplying a list of
 375       * $group names to grab.
 376       *
 377       * @param  string|array $lang_code The language code to fetch, or array(lang_code, override_lang_code)
 378       * @param  string|array $group     Comma-separated list or array of headings from which to extract strings
 379       * @param  string|array $filter    Comma-separated list or array of strings that should be returned
 380       * @return array
 381       */
 382  
 383      public function getPack($lang_code, $group = null, $filter = null)
 384      {
 385          if (is_array($lang_code)) {
 386              $lang_over = $lang_code[1];
 387              $lang_code = $lang_code[0];
 388          } else {
 389              $lang_over = $lang_code;
 390          }
 391  
 392          $lang_file = $this->findFilename($lang_code);
 393          $entries = array();
 394          $textpack = '';
 395  
 396          if ($lang_file && ($textpack = txp_get_contents($lang_file))) {
 397              $parser = new \Textpattern\Textpack\Parser();
 398              $parser->setOwner('');
 399              $parser->setLanguage($lang_over);
 400              $parser->parse($textpack, $group);
 401              $entries = $parser->getStrings($lang_over);
 402          }
 403  
 404          // Reindex the pack so it can be merged.
 405          $langpack = array();
 406          $filter = is_array($filter) ? $filter : do_list_unique($filter);
 407  
 408          foreach ($entries as $translation) {
 409              if (!$filter || in_array($translation['name'], $filter)) {
 410                  $langpack[$translation['name']] = $translation;
 411              }
 412          }
 413  
 414          return $langpack;
 415      }
 416  
 417      /**
 418       * Temporarily override a bunch of strings with a set from a different language.
 419       *
 420       * @param  string|null $lang   The language from which to extract strings.
 421       *                             If it matches the current language, nothing happens.
 422       *                             If null is passed in, the overwritten strings are restored.
 423       * @param  string|array $group List of groups (comma-separated or an array) to fetch in the new language.
 424       * @return null|true           Returns true if language swap took place, null otherwise.
 425       */
 426      function swapStrings($lang, $group = 'admin')
 427      {
 428          if ($lang && in_array($lang, $this->installed())) {
 429              $this->cachedStrings = $this->getStrings();
 430  
 431              // Override the language strings in the given groups with those of the passed language.
 432              $userPack = $this->getPack($lang, $group);
 433              $userStrings = array();
 434  
 435              foreach ($userPack as $key => $packBlock) {
 436                  $userStrings[$key] = $packBlock['data'];
 437              }
 438  
 439              $this->setPack($userStrings, true);
 440  
 441              return true;
 442          } elseif ($lang === null && $this->cachedStrings) {
 443              $this->setPack($this->cachedStrings, true);
 444              $this->cachedStrings = null;
 445  
 446              return true;
 447          }
 448  
 449          return null;
 450      }
 451  
 452      /**
 453       * Install a language pack from a file.
 454       *
 455       * @param string $lang_code The lang identifier to load
 456       */
 457  
 458      public function installFile($lang_code, $owner = '')
 459      {
 460          $langpack = $this->getPack($lang_code);
 461  
 462          if (empty($langpack)) {
 463              return false;
 464          }
 465  
 466          if ($lang_code !== TEXTPATTERN_DEFAULT_LANG) {
 467              // Load the fallback strings so we're not left with untranslated strings.
 468              // Note that the language is overridden to match the to-be-installed lang.
 469              $fallpack = $this->getPack(array(TEXTPATTERN_DEFAULT_LANG, $lang_code));
 470              $langpack += $fallpack;
 471          }
 472  
 473          return ($this->upsertPack($langpack, $owner) === false) ? false : true;
 474      }
 475  
 476  
 477      /**
 478       * Load localisation strings from a Textpack using the given language.
 479       *
 480       * @param   array  $textpack    The Textpack to install
 481       * @param   string $useLang     Import strings for this language
 482       * @package L10n
 483       */
 484  
 485      public function loadTextpack($textpack, $useLang = null)
 486      {
 487          global $textarray;
 488  
 489          $strings = array();
 490          $pack = new \Textpattern\Textpack\Parser();
 491          $pack->parse($textpack);
 492  
 493          if (!isset($useLang)) {
 494              $useLang = txpinterface === 'admin' ? get_pref('language_ui', TEXTPATTERN_DEFAULT_LANG) : get_pref('language', TEXTPATTERN_DEFAULT_LANG);
 495          }
 496  
 497          $wholePack = $pack->getStrings($useLang);
 498  
 499          if (!$wholePack) {
 500              $wholePack = $pack->getStrings(TEXTPATTERN_DEFAULT_LANG);
 501          }
 502  
 503          foreach ($wholePack as $entry) {
 504              $strings[$entry['name']] = $entry['data'];
 505          }
 506  
 507          // Append lang strings on-the-fly.
 508          $this->setPack($strings, true);
 509          $textarray += $strings;
 510      }
 511  
 512  
 513      /**
 514       * Install localisation strings from a Textpack.
 515       *
 516       * @param   string $textpack    The Textpack to install
 517       * @param   bool   $addNewLangs If TRUE, installs strings for any included language
 518       * @return  int                 Number of installed strings
 519       * @package L10n
 520       */
 521  
 522      public function installTextpack($textpack, $addNewLangs = false)
 523      {
 524          $parser = new \Textpattern\Textpack\Parser();
 525          $parser->setLanguage(get_pref('language', TEXTPATTERN_DEFAULT_LANG));
 526          $parser->parse($textpack);
 527          $packLanguages = $parser->getLanguages();
 528  
 529          if (empty($packLanguages)) {
 530              return 0;
 531          }
 532  
 533          $allpacks = array();
 534  
 535          foreach ($packLanguages as $lang_code) {
 536              $allpacks = array_merge($allpacks, $parser->getStrings($lang_code));
 537          }
 538  
 539          $installed_langs = $this->installed();
 540          $values = array();
 541  
 542          foreach ($allpacks as $translation) {
 543              extract(doSlash($translation));
 544  
 545              if (!$addNewLangs && !in_array($lang, $installed_langs)) {
 546                  continue;
 547              }
 548  
 549              $values[] = "('$name', '$lang', '$data', '$event', '$owner', NOW())";
 550          }
 551  
 552          $value = implode(',', $values);
 553  
 554          !$value || safe_query("INSERT INTO ".PFX."txp_lang
 555              (name, lang, data, event, owner, lastmod)
 556              VALUES $value
 557              ON DUPLICATE KEY UPDATE
 558              data=VALUES(data), event=VALUES(event), owner=VALUES(owner), lastmod=VALUES(lastmod)");
 559  
 560          return count($values);
 561      }
 562  
 563      /**
 564       * Insert or update a language pack.
 565       *
 566       * @param  array  $langpack The language pack to store
 567       * @param  string $langpack The owner to use if not in the pack
 568       * @return result set
 569       */
 570  
 571      public function upsertPack($langpack, $owner_ref = '')
 572      {
 573          $result = false;
 574  
 575          if ($langpack) {
 576              $values = array();
 577  
 578              foreach ($langpack as $key => $translation) {
 579                  extract(doSlash($translation));
 580  
 581                  $owner = empty($owner) ? doSlash($owner_ref) : $owner;
 582                  $lastmod = empty($lastmod) ? 'NOW()' : "'$lastmod'";
 583                  $values[] = "('$name', '$lang', '$data', '$event', '$owner', $lastmod)";
 584              }
 585  
 586              if ($values) {
 587                  $value = implode(',', $values);
 588                  $result = safe_query("INSERT INTO ".PFX."txp_lang
 589                      (name, lang, data, event, owner, lastmod)
 590                      VALUES $value
 591                      ON DUPLICATE KEY UPDATE
 592                      data=VALUES(data), event=VALUES(event), owner=VALUES(owner), lastmod=VALUES(lastmod)");
 593              }
 594          }
 595  
 596          return $result;
 597      }
 598  
 599      /**
 600       * Fetch the given language's strings from the database as an array.
 601       *
 602       * If no $events are specified, only appropriate strings for the current context
 603       * are returned. If the 'txpinterface' constant is 'public' only strings from
 604       * events 'common' and 'public' are returned.
 605       *
 606       * Note the returned array includes the language if the fallback has been used.
 607       * This ensures (as far as possible) a full complement of strings, regardless of
 608       * the degree of translation that's taken place in the desired $lang code.
 609       * Any holes can be mopped up by the default language.
 610       *
 611       * @param  string       $lang_code The language code
 612       * @param  array|string $events    A list of loaded events to extract
 613       * @param  string|array $filter    Comma-separated list or array of strings that should be returned
 614       * @return array
 615       */
 616  
 617      public function extract($lang_code, $events = null, $filter = null)
 618      {
 619          $where = array(
 620              "lang = '".doSlash($lang_code)."'",
 621              "name != ''",
 622          );
 623  
 624          if (txpinterface === 'admin') {
 625              $admin_events = array('admin-side', 'common');
 626  
 627              if ($events) {
 628                  $list = (is_array($events) ? $events : do_list_unique($events));
 629                  $admin_events = array_merge($admin_events, $list);
 630              }
 631  
 632              $events = $admin_events;
 633          } elseif ($events === null) {
 634              $events = array('public', 'common');
 635          } else {
 636              $events = is_array($events) ? $events : do_list_unique($events);
 637          }
 638  
 639          if ($events) {
 640              // For the time being, load any non-core (plugin) strings on every
 641              // page too. Core strings have no owner. Plugins installed since 4.6+
 642              // will have either the 'site' owner or their own plugin name.
 643              // Longer term, when all plugins have caught up with the event
 644              // naming convention, the owner clause can be removed.
 645              $where[] = "(event IN (".join(',', quote_list((array) $events)).")".($this->hasOwnerSupport() ? " OR owner != '')" : ')');
 646          }
 647  
 648          $out = array();
 649  
 650          $filter = is_array($filter) ? $filter : do_list_unique($filter);
 651  
 652          $rs = safe_rows_start("name, data", 'txp_lang', join(' AND ', $where));
 653  
 654          if (!empty($rs)) {
 655              while ($a = nextRow($rs)) {
 656                  if (!$filter || in_array($a['name'], $filter)) {
 657                      $out[$a['name']] = $a['data'];
 658                  }
 659              }
 660          }
 661  
 662          return $out;
 663      }
 664  
 665      /**
 666       * Load the given language's strings from the database into the class.
 667       *
 668       * Note the returned array includes the language if the fallback has been used.
 669       * This ensures (as far as possible) a full complement of strings, regardless of
 670       * the degree of translation that's taken place in the desired $lang code.
 671       * Any holes can be mopped up by the default language.
 672       *
 673       * @param  string       $lang_code The language code
 674       * @param  array|string $events    A list of loaded events to load
 675       * @see    extract()
 676       * @return array
 677       */
 678  
 679      public function load($lang_code, $events = null)
 680      {
 681          $loaded = isset($this->loaded[$lang_code]) ? $this->loaded[$lang_code] : null;
 682  
 683          if ($events === true) {
 684              return $loaded;
 685          }
 686  
 687          global $DB;
 688  
 689          if (!empty($DB)) {
 690              $this->strings = $this->extract($lang_code, $events);
 691              $this->loaded = array($lang_code => isset($events) ? do_list_unique($events) : array(null));
 692          }
 693  
 694          return $this->strings;
 695      }
 696  
 697      /**
 698       * Fetch the language strings from the loaded language.
 699       *
 700       * @return array
 701       */
 702  
 703      public function getStrings()
 704      {
 705          return $this->strings;
 706      }
 707  
 708      /**
 709       * Determine if a string key exists in the current pack
 710       *
 711       * @param  string  $var The string name to check
 712       * @return boolean
 713       */
 714  
 715      public function hasString($var)
 716      {
 717          $v = strtolower($var);
 718  
 719          return isset($this->strings[$v]);
 720      }
 721  
 722      /**
 723       * Return a localisation string.
 724       *
 725       * @param   string $var    String name
 726       * @param   array  $atts   Replacement pairs
 727       * @param   string $escape Convert special characters to HTML entities. Either "html" or ""
 728       * @return  string A localisation string
 729       * @package L10n
 730       */
 731  
 732      public function txt($var, $atts = array(), $escape = 'html')
 733      {
 734          global $textarray; // deprecated since 4.7
 735  
 736          $v = strtolower($var);
 737  
 738          if (isset($this->strings[$v])) {
 739              $out = $this->strings[$v];
 740          } else {
 741              $out = isset($textarray[$v]) ? $textarray[$v] : '';
 742          }
 743  
 744          if ($atts && $escape == 'html') {
 745              $atts = array_map('txpspecialchars', $atts);
 746          }
 747  
 748          if ($out !== '') {
 749              return $atts ? strtr($out, $atts) : $out;
 750          }
 751  
 752          if ($atts) {
 753              return $var.': '.join(', ', $atts);
 754          }
 755  
 756          return $var;
 757      }
 758  
 759      /**
 760       * Generate an array of languages and their localised names.
 761       *
 762       * @param  int $flags Logical OR list of flags indiacting the type of list to return:
 763       *                    TEXTPATTERN_LANG_ACTIVE: the active language
 764       *                    TEXTPATTERN_LANG_INSTALLED: all installed languages
 765       *                    TEXTPATTERN_LANG_AVAILABLE: all available languages in the file system
 766       * @return array
 767       */
 768  
 769      public function languageList($flags = null)
 770      {
 771          if ($flags === null) {
 772              $flags = TEXTPATTERN_LANG_ACTIVE | TEXTPATTERN_LANG_INSTALLED;
 773          }
 774  
 775          $installed_langs = $this->available((int)$flags);
 776          $vals = array();
 777  
 778          foreach ($installed_langs as $lang => $langdata) {
 779              $vals[$lang] = $langdata['name'];
 780  
 781              if (trim($vals[$lang]) == '') {
 782                  $vals[$lang] = $lang;
 783              }
 784          }
 785  
 786          ksort($vals);
 787          reset($vals);
 788  
 789          return $vals;
 790      }
 791  
 792      /**
 793       * Generate a &lt;select&gt; element of languages.
 794       *
 795       * @param  string $name  The HTML name and ID to assign to the select control
 796       * @param  string $val   The currently active language identifier (en-gb, fr, de, ...)
 797       * @param  int    $flags Logical OR list of flags indicating the type of list to return:
 798       *                       TEXTPATTERN_LANG_ACTIVE: the active language
 799       *                       TEXTPATTERN_LANG_INSTALLED: all installed languages
 800       *                       TEXTPATTERN_LANG_AVAILABLE: all available languages in the file system
 801       * @return string HTML
 802       */
 803  
 804      public function languageSelect($name, $val, $flags = null)
 805      {
 806          $vals = $this->languageList($flags);
 807  
 808          return selectInput($name, $vals, $val, false, true, $name);
 809      }
 810  
 811      /**
 812       * Determine if the class supports the 'owner' column or not.
 813       *
 814       * Only of use during upgrades from older versions to guard against errors.
 815       *
 816       * @return boolean
 817       */
 818  
 819      protected function hasOwnerSupport()
 820      {
 821          return (bool) version_compare(get_pref('version'), '4.6.0', '>=');
 822      }
 823  }

title

Description

title

Description

title

Description

title

title

Body