b2evolution PHP Cross Reference Blogging Systems

Source: /inc/locales/_locale.funcs.php - 1334 lines - 36522 bytes - Summary - Text - Print

Description: This file implements functions for handling locales and i18n. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements functions for handling locales and i18n.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @package evocore
  27   *
  28   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  29   * @author blueyed: Daniel HAHLER.
  30   * @author fplanque: Francois PLANQUE.
  31   *
  32   * @todo Make it a class / global object!
  33   *        - Provide (static) functions to extract .po files / generate _global.php files (single quoted strings!)
  34   *
  35   * @version $Id: _locale.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  36   */
  37  if( !defined('EVO_CONFIG_LOADED') ) die( 'Please, do not access this page directly.' );
  38  
  39  
  40  // DEBUG: (Turn switch on or off to log debug info for specified category)
  41  $GLOBALS['debug_locale'] = false;
  42  
  43  
  44  // LOCALIZATION:
  45  if( isset( $use_l10n ) && $use_l10n )
  46  { // We are going to use localization:
  47  
  48      /**
  49       * TRANSLATE!
  50       *
  51       * Translate a text to the desired locale or to the current locale.
  52       *
  53       * @param string String to translate, '' to get language file info (as in gettext spec)
  54       * @param string locale to translate to, '' to use current locale
  55       * @param array Array containing the following keys (all optional):
  56       *              - 'ext_transarray': A reference to an alternate array
  57       *                                  to use for the caching of the
  58       *                                  translated strings or NULL to use
  59       *                                  the internal array.
  60       *              - 'alt_basedir': Alternate base directory to search
  61       *                               for translations, e. g. a plugin or
  62       *                               skin directory.
  63       *              - 'for_helper': (boolean) Is the translation for the b2evoHelper object?
  64       * @return string The translated string or the original string on error.
  65       *
  66       * @internal The last parameter/its 'alt_basedir' key is used by
  67       *           Plugin::T_() and Skin::T_().
  68       */
  69      function T_( $string, $req_locale = '', $params = array() )
  70      {
  71          /**
  72           * The translations keyed by locale.
  73           *
  74           * This array is only used if $params['ext_transarray'] === NULL.
  75           *
  76           * @var array
  77           * @static
  78           */
  79          static $_trans = array();
  80  
  81          global $current_locale, $locales, $locales_path, $plugins_path;
  82          global $evo_charset, $Debuglog;
  83  
  84          $params = array_merge( array(
  85                                  'ext_transarray' => NULL,
  86                                  'alt_basedir'    => '',
  87                                  'for_helper'     => false,
  88                                  ), $params );
  89  
  90          if( empty( $req_locale ) )
  91          { // By default we use the current locale
  92              if( empty( $current_locale ) )
  93              { // don't translate if we have no locale
  94                  return $string;
  95              }
  96  
  97              $req_locale = $current_locale;
  98          }
  99  
 100          if( ! isset( $locales[$req_locale]['messages'] ) )
 101          {
 102              $Debuglog->add( 'No messages file path for locale. $locales["'
 103                      .$req_locale.'"] is '.var_export( @$locales[$req_locale], true ), 'locale' );
 104  
 105              if( ! empty( $evo_charset ) ) // this extra check is needed, because $evo_charset may not yet be determined.. :/
 106              {
 107                  $string = convert_charset( $string, $evo_charset, 'iso-8859-1' );
 108              }
 109              return $string;
 110          }
 111  
 112          $messages = $locales[$req_locale]['messages'];
 113  
 114          if ( is_null( $params['ext_transarray'] ) )
 115          {    // use our array
 116              //$Debuglog->add( 'Using internal array', 'locale' );
 117              $trans = & $_trans;
 118          }
 119          else
 120          {    // use external array:
 121              //$Debuglog->add( 'Using external array', 'locale' );
 122              $trans = & $params['ext_transarray'];
 123          }
 124  
 125          if( ! isset( $trans[ $messages ] ) )
 126          { // Translations for current locale have not yet been loaded:
 127              if ( $params['alt_basedir'] != '' )
 128              {    // Load the translation file from the alternative base dir:
 129                  //$Debuglog->add( 'Using alternative basedir ['.$params['alt_basedir'].']', 'locale' );
 130                  $path = $params['alt_basedir'].'/locales/'.$messages.'/_global.php';
 131              }
 132              else
 133              {    // Load our global translation file.
 134                  $path = $locales_path.$messages.'/_global.php';
 135              }
 136  
 137              if( file_exists($path) && is_readable($path) )
 138              {
 139                  $Debuglog->add( 'T_: Loading file: '.$path, 'locale' );
 140                  include_once $path;
 141              }
 142              else
 143              {
 144                  $Debuglog->add( 'T_: Messages file does not exist or is not readable: '.$path, 'locale' );
 145              }
 146              if( ! isset($trans[ $messages ] ) )
 147              { // Still not loaded... file doesn't exist, memorize that no translations are available
 148                  // echo 'file not found!';
 149                  $trans[ $messages ] = array();
 150              }
 151              else
 152              {
 153                  if( ! isset($trans[$messages]['__meta__']) )
 154                  { // Unknown/old messages format (< version 1):
 155                      $Debuglog->add( 'Found deprecated messages format (no __meta__ info).', 'locale' );
 156                      // Translate keys (e.g. 'foo\nbar') to real strings ("foo\nbar")
 157                      // Doing this here for all strings, is actually faster than doing it on key lookup (like it has been done before always)
 158                      foreach($trans[$messages] as $k => $v)
 159                      {
 160                          if( ($pos = strpos($k, '\\')) === false )
 161                          { // fast-path-skip
 162                              continue;
 163                          }
 164                          // Replace string as done in the good old days:
 165                          $new_k = str_replace( array('\n', '\r', '\t'), array("\n", "\r", "\t"), $k );
 166                          if( $new_k != $k )
 167                          {
 168                              $trans[$messages][$new_k] = $v;
 169                              unset($trans[$messages][$k]);
 170                          }
 171                      }
 172                  }
 173              }
 174          }
 175  
 176          // sam2kb> b2evolution creates _global.php files with "\n" line breaks, and we must normalize newlines
 177          // in supplied string before trying to translate it. Otherwise strings won't match.
 178          // fp> TODO: this is not really satisfying in the long term. We need our own
 179          // parser that will extract T_() TS_() NT_() etc string and create a normalized potfile.
 180          // Actually it sgould create several potfiles. One for general use, one for admin, one for install, etc.
 181          // That way translators can concentrate on the most essential stuff first.
 182          $search_string = str_replace( array("\r\n", "\r"), "\n", $string );
 183  
 184          if( isset( $trans[ $messages ][ $search_string ] ) )
 185          { // If the string has been translated:
 186              //$Debuglog->add( 'String ['.$string.'] found', 'locale' );
 187              $r = $trans[ $messages ][ $search_string ];
 188              if( isset($trans[$messages]['__meta__']['charset']) )
 189              { // new format: charset in meta data:
 190                  $messages_charset = $trans[$messages]['__meta__']['charset'];
 191              }
 192              else
 193              { // old format.. extract charset from content type or fall back to setting from global locale definition:
 194                  $meta = $trans[$messages][''];
 195                  if( preg_match( '~^Content-Type: text/plain; charset=(.*);?$~m', $meta, $match ) )
 196                  {
 197                      $messages_charset = $match[1];
 198                  }
 199                  else
 200                  {
 201                      $messages_charset = $locales[$req_locale]['charset'];
 202                  }
 203                  // Set it accordingly to new format.
 204                  $trans[$messages]['__meta__']['charset'] = $messages_charset;
 205              }
 206          }
 207          else
 208          {
 209              //$Debuglog->add( 'String ['.$string.'] not found', 'locale' );
 210              // Return the English string:
 211              $r = $string;
 212              $messages_charset = 'iso-8859-1'; // our .php file encoding
 213          }
 214  
 215          if( ! empty($evo_charset) ) // this extra check is needed, because $evo_charset may not yet be determined.. :/
 216          {
 217              $r = convert_charset( $r, $evo_charset, $messages_charset );
 218          }
 219          else
 220          {
 221              $Debuglog->add(sprintf('Warning: evo_charset not set to translate "%s"', htmlspecialchars($string)), 'locale');
 222          }
 223  
 224          if( $params['for_helper'] )
 225          { // translation is for the b2evoHelper object
 226              add_js_translation( $string, $r );
 227          }
 228  
 229          //$Debuglog->add( 'Result: ['.$r.']', 'locale' );
 230          return $r;
 231      }
 232  
 233  }
 234  else
 235  { // We are not localizing at all:
 236  
 237      /**
 238       * @ignore
 239       */
 240      function T_( $string, $req_locale = '', $params = array() )
 241      {
 242          return $string;
 243      }
 244  
 245  }
 246  
 247  /**
 248   * Translate and escape single quotes.
 249   *
 250   * This is to be used mainly for Javascript strings.
 251   *
 252   * @uses T_()
 253   * @param string String to translate
 254   * @param string Locale to use
 255   * @param array  See {@link T_()}
 256   * @return string The translated and escaped string.
 257   */
 258  function TS_( $string, $req_locale = '', $params = array() )
 259  {
 260      return str_replace( "'", "\\'", T_( $string, $req_locale, $params ) );
 261  }
 262  
 263  
 264  /**
 265   * Temporarily switch to another locale
 266   *
 267   * Calls can be nested, see {@link locale_restore_previous()}.
 268   *
 269   * @param string locale to activate
 270   * @return boolean true on success, false on failure
 271   */
 272  function locale_temp_switch( $locale )
 273  {
 274      global $saved_locales, $current_locale, $Timer;
 275  
 276      // $Timer->resume( 'locale_temp_switch' );
 277  
 278      if( !isset( $saved_locales ) || ! is_array( $saved_locales ) )
 279      {
 280          $saved_locales = array();
 281      }
 282  
 283      $prev_locale = $current_locale;
 284      if( locale_activate( $locale ) )
 285      {
 286          array_push( $saved_locales, $prev_locale );
 287          return true;
 288      }
 289  
 290      // $Timer->stop( 'locale_temp_switch' );
 291      return false;
 292  }
 293  
 294  
 295  /**
 296   * Restore the locale in use before the switch
 297   *
 298   * @see locale_temp_switch()
 299   * @return boolean true on success, false on failure (no locale stored before)
 300   */
 301  function locale_restore_previous()
 302  {
 303      global $saved_locales;
 304  
 305      if( !empty( $saved_locales ) && is_array( $saved_locales ) )
 306      {
 307          locale_activate( array_pop( $saved_locales ) );
 308          return true;
 309      }
 310      return false;
 311  }
 312  
 313  
 314  /**
 315   * Activate a locale.
 316   *
 317   * @todo dh> this should make sure, that e.g. "charset" is set for the locale in {@link $locales}. See http://forums.b2evolution.net/viewtopic.php?p=43980#43980
 318   *
 319   * @param string locale to activate
 320   * @param boolean True on success/change, false on failure (if already set or not existant)
 321   */
 322  function locale_activate( $locale )
 323  {
 324      global $locales, $current_locale, $current_charset;
 325  
 326      if( $locale == $current_locale
 327              || empty( $locale )
 328              || ! isset( $locales[$locale] ) )
 329      {
 330          return false;
 331      }
 332  
 333      // Memorize new locale:
 334      $current_locale = $locale;
 335      // Memorize new charset:
 336      $current_charset = $locales[ $locale ][ 'charset' ];
 337      return true;
 338  }
 339  
 340  
 341  /**
 342   * locale_by_lang(-)
 343   *
 344   * Find first locale matching lang
 345   */
 346  function locale_by_lang( $lang, $fallback_to_default = true )
 347  {
 348      global $locales, $default_locale;
 349  
 350      foreach( $locales as $locale => $locale_params )
 351      {
 352          if( substr( $locale, 0 ,2 ) == $lang )
 353          { // found first matching locale
 354              return $locale;
 355          }
 356      }
 357  
 358      // Not found...
 359      if( $fallback_to_default )
 360          return $default_locale;
 361      else
 362          return $lang;
 363  }
 364  
 365  
 366  /**
 367   * Displays/Returns the current locale. (for backward compatibility)
 368   *
 369   * This is for HTML lang attributes
 370   *
 371   * @param boolean true (default) if we want it to be outputted
 372   * @return string current locale, if $disp = false
 373   */
 374  function locale_lang( $disp = true )
 375  {
 376      global $current_locale;
 377  
 378      if( $disp )
 379          echo $current_locale;
 380      else
 381          return $current_locale;
 382  }
 383  
 384  
 385  /**
 386   * Returns the charset of the current locale
 387   */
 388  function locale_charset( $disp = true )
 389  {
 390      global $current_charset;
 391  
 392      if( $disp )
 393          echo $current_charset;
 394      else
 395          return $current_charset;
 396  }
 397  
 398  
 399  /**
 400   * Returns the current locale's default date format
 401   * @param string Locale, must be set in {@link $locales}
 402   * @return string Date format of the locale, e.g. 'd.m.Y'
 403   */
 404  function locale_datefmt( $locale = NULL )
 405  {
 406      global $locales;
 407  
 408      if( empty($locale) )
 409      {
 410          global $current_locale;
 411          $locale = $current_locale;
 412      }
 413  
 414      return $locales[$locale]['datefmt'];
 415  }
 416  
 417  
 418  /**
 419   * Returns the current locale's default time format
 420   */
 421  function locale_timefmt()
 422  {
 423      global $locales, $current_locale;
 424  
 425      return $locales[$current_locale]['timefmt'];
 426  }
 427  
 428  /**
 429   * Returns the current locale's default short time format
 430   */
 431  function locale_shorttimefmt()
 432  {
 433      global $locales, $current_locale;
 434  
 435      return str_replace( ':s', '', $locales[$current_locale]['timefmt'] );
 436  }
 437  
 438  
 439  function locale_datetimefmt( $separator = ' ' )
 440  {
 441      global $locales, $current_locale;
 442  
 443      return $locales[$current_locale]['datefmt'].$separator.$locales[$current_locale]['timefmt'];
 444  }
 445  
 446  /**
 447   * Returns the current locale's start of week
 448   *
 449   * @return integer 0 for Sunday, 1 for Monday
 450   */
 451  function locale_startofweek()
 452  {
 453      global $locales, $current_locale;
 454  
 455      return (int)$locales[$current_locale]['startofweek'];
 456  }
 457  
 458  
 459  /**
 460   * Get the country locale
 461   *
 462   * @param string locale to use, '' for current
 463   *
 464   * @return string country locale
 465   */
 466  function locale_country( $locale = '' )
 467  {
 468      global $current_locale;
 469  
 470      if( empty($locale) ) $locale = $current_locale;
 471  
 472      return substr( $locale, 3, 2 );
 473  }
 474  
 475  
 476  /**
 477   *    Get the locale country dialing code
 478   */
 479  function locale_dialing_code( $locale = '' )
 480  {
 481          global $current_locale, $CountryCache;
 482  
 483          if( empty($locale) )
 484          {
 485              $locale = locale_country();
 486          }
 487  
 488          $edited_Country = $CountryCache->get_by_ID( $locale);
 489  
 490          return $edited_Country->dialing_code;
 491  }
 492  
 493  
 494  /**
 495   * Template function: Display locale flag
 496   *
 497   * @param string locale to use, '' for current
 498   * @param string DEPRECATED PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 499   * @param string name of class for IMG tag   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 500   * @param string deprecated HTML align attribute   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 501   * @param boolean to echo or not
 502   * @param mixed use absolute url (===true) or path to flags directory (used in installer)   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 503   */
 504  function locale_flag( $locale = '', $collection = 'deprecated_param', $class = 'flag', $align = '', $disp = true, $absoluteurl = true )
 505  {
 506      global $locales, $current_locale, $country_flags_bg;
 507  
 508      if( empty( $locale ) )
 509      {
 510          $locale = $current_locale;
 511      }
 512  
 513      // extract flag name:
 514      $country_code = strtolower( substr( $locale, 3, 2 ) );
 515  
 516      $flag_attribs = array(
 517          'class' => 'flag',
 518          'title' => isset($locales[$locale]['name']) ? $locales[$locale]['name'] : $locale,
 519      );
 520  
 521      if( isset( $country_flags_bg[ $country_code ] ) )
 522      {    // Set background-position from config
 523          $flag_attribs['style'] = 'background-position:'.$country_flags_bg[ $country_code ];
 524      }
 525  
 526      $r = '<span'.get_field_attribs_as_string( $flag_attribs ).'></span>';
 527  
 528      if( $disp )
 529          echo $r;   // echo it
 530      else
 531          return $r; // return it
 532  }
 533  
 534  
 535  /**
 536   * [callback function] Outputs an <option> set with default locale selected.
 537   * Optionally returns an array with a locale key and name if there's only one enabled locale.
 538   *
 539   * @param string default value
 540   * @param boolean echo output?
 541   * @param boolean Return array (locale key + name) if there's only one enabled locale?
 542   * @return string|array The options string or an array (locale key + name) if there's only one enabled locale and $array_if_onelocale == true.
 543   */
 544  function locale_options( $default = '', $disp = true, $array_if_onelocale = false )
 545  {
 546      global $locales, $default_locale;
 547  
 548      if( empty( $default ) ) $default = $default_locale;
 549  
 550      $r = '';
 551      $enabled_count = 0;
 552      $enabled_lastkey = '';
 553  
 554      foreach( $locales as $this_localekey => $this_locale )
 555      {
 556          if( $this_locale['enabled'] || $this_localekey == $default )
 557          {
 558              $r .= '<option value="'. $this_localekey. '"';
 559              if( $this_localekey == $default )
 560                  $r .= ' selected="selected"';
 561              $r .= '>'. T_($this_locale['name']). '</option>';
 562  
 563              ++$enabled_count;
 564              $enabled_lastkey = $this_localekey;
 565          }
 566      }
 567  
 568      if( $disp )
 569      {    // the result must be displayed
 570          echo $r;
 571      }
 572  
 573      if ( $array_if_onelocale && $enabled_count == 1 )
 574      {    // We've only one enabled locale:
 575          return array( $enabled_lastkey, $locales[$enabled_lastkey]['name'] );
 576      }
 577      else
 578      {    // Return the string.
 579          return $r;
 580      }
 581  }
 582  
 583  
 584  /**
 585   * [callback function] Returns an <option> set with default locale selected
 586   *
 587   * @param string default value
 588   */
 589  function locale_options_return( $default = '' )
 590  {
 591      $r = locale_options( $default, false );
 592      return $r;
 593  }
 594  
 595  
 596  /**
 597   * Detect language from HTTP_ACCEPT_LANGUAGE
 598   *
 599   * HTTP_ACCEPT_LANGUAGE is sorted by prio and then the best match is used
 600   * (either full locale ("en-US") or best fitting locale for a short one ("en").
 601   *
 602   * This gets tested in {@link test_locale_from_httpaccept()}.
 603   *
 604   * @author Rewritten by blueyed in Revision 1.42
 605   *
 606   * @return string Locale made out of HTTP_ACCEPT_LANGUAGE or $default_locale, if no match
 607   */
 608  function locale_from_httpaccept()
 609  {
 610      global $locales, $default_locale;
 611      if( isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) )
 612      {
 613          $accept = strtolower( $_SERVER['HTTP_ACCEPT_LANGUAGE'] );
 614  
 615          // Create list of accepted locales.
 616          if( ! preg_match_all('/([a-z]{1,8}(?:-[a-z]{1,8})?)\s*(?:;\s*q\s*=\s*(1|0(?:\.[0-9]+)?))?/i', $accept, $accept_list) )
 617          {
 618              return $default_locale;
 619          }
 620          // Create list of enabled locales.
 621          $enabled_locales = array();
 622          foreach($locales as $k => $v)
 623          {
 624              if( empty($v['enabled']) )
 625              {
 626                  continue;
 627              }
 628              $enabled_locales[strtolower($k)] = $k;
 629          }
 630          if( empty($enabled_locales) )
 631          {
 632              return $default_locale;
 633          }
 634          // Build mapping of short code to long code(s)
 635          $short_locales = array();
 636          foreach($enabled_locales as $v)
 637          {
 638              if( $pos = strpos($v, '-') )
 639              {
 640                  $short = substr($v, 0, $pos);
 641                  $short_locales[$short][] = $v;
 642              }
 643          }
 644          // Create "locale" => "prio" list
 645          $accept_list = array_combine($accept_list[1], $accept_list[2]);
 646          $maxq = count($accept_list)+1;
 647          foreach( $accept_list as $k => $v )
 648          {
 649              if( $v === '' )
 650              { // should be kept in order
 651                  $accept_list[$k] = $maxq--;
 652              }
 653              elseif( $v == 0 )
 654              { // not acceptable (RFC 2616)
 655                  unset($accept_list[$k]);
 656              }
 657          }
 658          arsort($accept_list);
 659          $accept_list = array_values(array_keys($accept_list));
 660  
 661          // Go through the list of accepted locales and find best match.
 662          for( $i = 0, $n = count($accept_list); $i<$n; $i++ )
 663          {
 664              $test = $accept_list[$i];
 665  
 666              if( isset($enabled_locales[$test]) )
 667              { // the accepted locale is enabled and a full match
 668                  return $enabled_locales[$test];
 669              }
 670              if( isset($short_locales[$test]) )
 671              { // this is a short locale: find the best/first match in accepted locales
 672                  $first = NULL;
 673                  foreach($short_locales[$test] as $v)
 674                  {
 675                      $pos = array_search(strtolower($v), $accept_list);
 676                      if( $pos !== false && ( ! isset($first) || $pos < $first ) )
 677                      {
 678                          $first = $pos;
 679                      }
 680                  }
 681                  if( isset($first) )
 682                  { // found exact match, via short locale.
 683                      return $enabled_locales[$accept_list[$first]];
 684                  }
 685                  if( isset($enabled_locales[$test.'-'.$test]) )
 686                  { // test for e.g. "de-DE" when only "de" is accepted
 687                      return $enabled_locales[$test.'-'.$test];
 688                  }
 689                  // Fallback: use first enabled locale matching the short accepted
 690                  return $short_locales[$test][0];
 691              }
 692          }
 693      }
 694      return $default_locale;
 695  }
 696  
 697  
 698  /**
 699   * user sort function to sort locales by priority
 700   *
 701   * 1 is highest priority.
 702   *
 703   */
 704  function locale_priosort( $a, $b )
 705  {
 706      return $a['priority'] - $b['priority'];
 707  }
 708  
 709  
 710  /**
 711   * load locales from DB into $locales array. Also sets $default_locale.
 712   *
 713   * @return mixed new default locale on succes, false on failure
 714   */
 715  function locale_overwritefromDB()
 716  {
 717      global $DB, $locales, $default_locale, $Settings, $Debuglog;
 718  
 719      $usedprios = array();  // remember which priorities are used already.
 720      $priocounter = 0;
 721      $query = 'SELECT
 722                          loc_locale, loc_charset, loc_datefmt, loc_timefmt, loc_startofweek,
 723                          loc_name, loc_messages, loc_priority, loc_transliteration_map, loc_enabled
 724                          FROM T_locales ORDER BY loc_priority';
 725  
 726      foreach( $DB->get_results( $query, ARRAY_A ) as $row )
 727      { // Loop through loaded locales:
 728  
 729          if( $row['loc_priority'] == $priocounter )
 730          { // priority conflict (the same)
 731              $priocounter++;
 732          }
 733          else
 734          {
 735              $priocounter = $row['loc_priority'];
 736          }
 737  
 738          //remember that we used this
 739          $usedprios[] = $priocounter;
 740  
 741          $transliteration_map = '';
 742          // Try to unserialize the value
 743          if( ($r = @unserialize(@base64_decode($row[ 'loc_transliteration_map' ]))) !== false )
 744          {
 745              $transliteration_map = $r;
 746          }
 747  
 748          $locales[ $row['loc_locale'] ] = array(
 749                  'charset'     => $row[ 'loc_charset' ],
 750                  'datefmt'     => $row[ 'loc_datefmt' ],
 751                  'timefmt'     => $row[ 'loc_timefmt' ],
 752                  'startofweek' => $row[ 'loc_startofweek' ],
 753                  'name'        => $row[ 'loc_name' ],
 754                  'messages'    => $row[ 'loc_messages' ],
 755                  'transliteration_map' => $transliteration_map,
 756                  'priority'    => $priocounter,
 757                  'enabled'     => $row[ 'loc_enabled' ],
 758                  'fromdb'      => 1
 759              );
 760      }
 761  
 762      // set default priorities, if nothing was set in DB.
 763      // Missing "priority gaps" will get filled here.
 764      if( $DB->num_rows != count($locales) )
 765      { // we have locales from conf file that need a priority
 766          $priocounter = 1;
 767          foreach( $locales as $lkey => $lval )
 768          { // Loop through memory locales:
 769              if( !isset($lval['priority']) )
 770              { // Found one that has no assigned priority
 771                  while( in_array( $priocounter, $usedprios ) )
 772                  {
 773                      $priocounter++;
 774                  }
 775                  // Priocounter has max value
 776                  $locales[$lkey]['priority'] = $priocounter;
 777                  $usedprios[] = $priocounter;
 778              }
 779          }
 780      }
 781  
 782      // sort by priority
 783      uasort( $locales, 'locale_priosort' );
 784  
 785      // overwrite default_locale from DB settings - if enabled.
 786      // Checks also if previous $default_locale is enabled. Defaults to en-EU, even if not enabled.
 787      $locale_fromdb = $Settings->get('default_locale');
 788  
 789      if( $locale_fromdb )
 790      {
 791          if( !isset( $locales[$locale_fromdb] ) )
 792          {
 793              $Debuglog->add( 'Default locale ['.$locale_fromdb.'] from general settings is not available.', 'locale' );
 794              return false;
 795          }
 796          else
 797          {
 798              $default_locale = $locale_fromdb;
 799              return $default_locale;
 800          }
 801      }
 802  }
 803  
 804  
 805  /**
 806   * Write $locales array to DB table
 807   */
 808  function locale_updateDB()
 809  {
 810      global $locales, $DB;
 811  
 812      $templocales = $locales;
 813  
 814      $lnr = 0;
 815      // Loop through list of all HTTP POSTed params:
 816      foreach( $_POST as $pkey => $pval )
 817      {
 818          if( ! preg_match('/loc_(\d+)_(.*)/', $pkey, $matches) )
 819          {
 820              continue;
 821          }
 822  
 823          // This is a locale related parameter, get it now:
 824          $pval = param( $pkey, 'string', '' );
 825  
 826          $lfield = $matches[2];
 827  
 828          if( $matches[1] != $lnr )
 829          { // we have a new locale
 830              $lnr = $matches[1];
 831              $plocale = $pval;
 832  
 833              // checkboxes default to 0
 834              $templocales[ $plocale ]['enabled'] = 0;
 835          }
 836          elseif( $lnr != 0 )  // be sure to have catched a locale before
 837          {
 838              if( $lfield == 'startofweek' && ( $lfield < 0 || $lfield > 6 ) )
 839              { // startofweek must be between 0 and 6
 840                  continue;
 841              }
 842              $templocales[ $plocale ][$lfield] = $pval;
 843          }
 844      }
 845  
 846      $locales = $templocales;
 847  
 848      $query = "REPLACE INTO T_locales ( loc_locale, loc_charset, loc_datefmt, loc_timefmt, loc_startofweek, loc_name, loc_messages, loc_priority, loc_transliteration_map, loc_enabled ) VALUES ";
 849      foreach( $locales as $localekey => $lval )
 850      {
 851          if( empty($lval['messages']) )
 852          { // if not explicit messages file is given we'll translate the locale
 853              $lval['messages'] = strtr($localekey, '-', '_');
 854          }
 855  
 856          $transliteration_map = '';
 857          if( !empty($lval['transliteration_map']) )
 858          {
 859              if( is_string($lval['transliteration_map']) )
 860              {    // The value is already serialized and encoded
 861                  $transliteration_map = $lval['transliteration_map'];
 862              }
 863              else
 864              {    // Encode the value
 865                  $transliteration_map = base64_encode( serialize($lval['transliteration_map']) );
 866              }
 867          }
 868  
 869          $query .= '(
 870              '.$DB->quote($localekey).',
 871              '.$DB->quote($lval['charset']).',
 872              '.$DB->quote($lval['datefmt']).',
 873              '.$DB->quote($lval['timefmt']).',
 874              '.$DB->quote($lval['startofweek']).',
 875              '.$DB->quote($lval['name']).',
 876              '.$DB->quote($lval['messages']).',
 877              '.$DB->quote($lval['priority']).',
 878              '.$DB->quote($transliteration_map).',
 879              '.$DB->quote($lval['enabled']).'
 880          ), ';
 881      }
 882      $query = substr($query, 0, -2);
 883      $q = $DB->query($query);
 884  
 885      return (bool)$q;
 886  }
 887  
 888  
 889  /**
 890   * Convert a string from one charset to another.
 891   *
 892   * @todo Implement iconv and PHP mapping tables
 893   *
 894   * @see can_convert_charsets()
 895   * @param string String to convert
 896   * @param string Target charset (TO)
 897   * @param string Source charset (FROM)
 898   * @return string Encoded string (if it cannot be converted it's the original one)
 899   */
 900  function convert_charset( $string, $dest_charset, $src_charset )
 901  {
 902      if( isset($GLOBALS['Timer']) )
 903      {
 904          $GLOBALS['Timer']->resume('convert_charset', false );
 905      }
 906      if( $dest_charset == $src_charset || $dest_charset == '' /* may happen if $evo_charset is not defined yet */ )
 907      { // no conversation required
 908          if( isset($GLOBALS['Timer']) )
 909          {
 910              $GLOBALS['Timer']->pause('convert_charset', false );
 911          }
 912          return $string;
 913      }
 914  
 915      if( function_exists('mb_convert_variables') )
 916      { // mb_string extension:
 917          mb_convert_variables( $dest_charset, $src_charset, $string );
 918      }
 919      // pre_dump( $dest_charset, $src_charset, $string );
 920  
 921      if( isset($GLOBALS['Timer']) )
 922      {
 923          $GLOBALS['Timer']->pause('convert_charset', false );
 924      }
 925      return $string;
 926  }
 927  
 928  
 929  /**
 930   * Can we convert from charset A to charset B?
 931   * @param string Target charset (TO)
 932   * @param string Source charset (FROM)
 933   * @return boolean
 934   */
 935  function can_convert_charsets( $dest_charset, $src_charset )
 936  {
 937      if( empty($dest_charset) || empty($src_charset) )
 938      {
 939          return false;
 940      }
 941  
 942      if( function_exists('mb_internal_encoding') )
 943      { // mb_string extension:
 944          $orig = mb_internal_encoding();
 945  
 946          $r = false;
 947          if( @mb_internal_encoding($dest_charset) && @mb_internal_encoding($src_charset) )
 948          { // we can set both encodings, so we should be able to convert:
 949              $r = true;
 950          }
 951  
 952          mb_internal_encoding($orig);
 953          return $r;
 954      }
 955  
 956      return false;
 957  }
 958  
 959  
 960  /**
 961   * Can we check for valid encodings of strings, using {@link check_encoding()}?
 962   *
 963   * @return boolean
 964   */
 965  function can_check_encoding()
 966  {
 967      return function_exists('mb_check_encoding');
 968  }
 969  
 970  
 971  /**
 972   * Check if the string is valid for the specified encoding.
 973   *
 974   * @param string String to check
 975   * @param string Encoding to check
 976   * @return
 977   */
 978  function check_encoding($str, $encoding)
 979  {
 980      if( function_exists('mb_check_encoding') )
 981      {
 982          return mb_check_encoding($str, $encoding);
 983      }
 984  
 985      return NULL;
 986  }
 987  
 988  
 989  /**
 990   * Init charset handling between Input/Output ($io_charset) and the internal
 991   * handling ($evo_charset).
 992   *
 993   * Check and possibly adjust {@link $evo_charset}.
 994   *
 995   * @staticvar boolean Used to only start mb_output_handler once
 996   * @param string I/O (input/output) charset to use
 997   * @return boolean true, if encoding has been changed
 998   */
 999  function init_charsets( $req_io_charset )
1000  {
1001      static $mb_output_handler_started;
1002      global $io_charset, $evo_charset, $Debuglog, $DB;
1003      global $force_io_charset_if_accepted;
1004  
1005      if( $req_io_charset == $io_charset )
1006      { // no conversation/init needed
1007          return false;
1008      }
1009  
1010      // check, if we want to force a specific charset (e.g. 'utf-8'):
1011      if( ! empty($force_io_charset_if_accepted) )
1012      { // we want to force a specific charset:
1013          if( ! isset($_SERVER['HTTP_ACCEPT_CHARSET']) // all allowed
1014              || preg_match( '~\b(\*|'.preg_quote( $force_io_charset_if_accepted, '~' ).')\b~', $_SERVER['HTTP_ACCEPT_CHARSET'] ) )
1015          {
1016              $req_io_charset = $force_io_charset_if_accepted; // pretend that the first one has been requested
1017          }
1018      }
1019  
1020      if( $req_io_charset == $io_charset )
1021      { // no conversation/init needed
1022          return false;
1023      }
1024  
1025      $io_charset = $req_io_charset;
1026  
1027      if( empty($evo_charset) )
1028      { // empty evo_charset follows I/O charset:
1029          // TODO: $evo_charset will not follow, if it has followed before.. (because not empty anymore)
1030          $Debuglog->add( '$evo_charset follows $io_charset ('.$io_charset.').', array('locale') );
1031          $evo_charset = $io_charset;
1032      }
1033      elseif( $evo_charset != $io_charset )
1034      { // we have to convert for I/O
1035          // TODO: dh> $io_charset has to forcefully follow $evo_charset, if we cannot convert, e.g. utf-8/iso-8859-1
1036          if( ! function_exists('mb_convert_encoding') )
1037          {
1038              $Debuglog->add( '$evo_charset differs from $io_charset, but mbstrings is not available - cannot convert I/O to internal charset!', array('errors','locale') );
1039              $evo_charset = $io_charset; // we cannot convert I/O to internal charset
1040          }
1041          else
1042          { // check if the encodings are supported:
1043              // NOTE: mb_internal_encoding() is the best way to find out if the encoding is supported
1044              $old_mb_internal_encoding = mb_internal_encoding();
1045              if( ! @mb_internal_encoding($io_charset) )
1046              {
1047                  $Debuglog->add( 'Cannot I/O convert because I/O charset ['.$io_charset.'] is not supported by mbstring!', array('error','locale') );
1048                  $evo_charset = $io_charset;
1049                  mb_internal_encoding($old_mb_internal_encoding);
1050              }
1051              elseif( ! @mb_internal_encoding($evo_charset) )
1052              {
1053                  $Debuglog->add( 'Cannot I/O convert because $evo_charset='.$evo_charset.' is not supported by mbstring!', array('error','locale') );
1054                  $evo_charset = $io_charset;
1055                  mb_internal_encoding($old_mb_internal_encoding);
1056              }
1057              else
1058              { // we can convert between I/O
1059                  mb_http_output( $io_charset );
1060                  if( ! $mb_output_handler_started )
1061                  {
1062                      ob_start( 'mb_output_handler' ); // NOTE: this will send a Content-Type header by itself for "text/..."
1063                      $mb_output_handler_started = true;
1064                      $Debuglog->add( 'Started mb_output_handler.', 'locale' );
1065                  }
1066              }
1067          }
1068      }
1069  
1070      // Make sure the DB send us text in the same charset as $evo_charset (override whatever may have been set before)
1071      if( isset($DB) ) // not available in /install/index.php
1072      {    // Set encoding for MySQL connection:
1073          $DB->set_connection_charset( $evo_charset );
1074      }
1075  
1076      $Debuglog->add( 'evo_charset: '.$evo_charset, 'locale' );
1077      $Debuglog->add( 'io_charset: '.$io_charset, 'locale' );
1078  
1079      return true;
1080  }
1081  
1082  
1083  /**
1084   * Load available locale definitions
1085   */
1086  function locales_load_available_defs()
1087  {
1088      global $locales_path;
1089      global $locales;
1090  
1091      // This is where language packs will store their locale defintions:
1092      $locale_defs = array();
1093  
1094      // Get all locale folder names:
1095      $filename_params = array(
1096              'inc_files'    => false,
1097              'recurse'    => false,
1098              'basename'    => true,
1099          );
1100      $locale_folders = get_filenames( $locales_path, $filename_params );
1101      // Go through all locale folders:
1102      foreach( $locale_folders as $locale_folder )
1103      {
1104          //pre_dump( $locale_folder );
1105          $ad_locale_folder = $locales_path.'/'.$locale_folder;
1106          // Get files in folder:
1107          $filename_params = array(
1108                  'inc_dirs'    => false,
1109                  'recurse'    => false,
1110                  'basename'    => true,
1111              );
1112          $locale_def_files = get_filenames( $ad_locale_folder, $filename_params );
1113          // Go through files in locale folder:
1114          foreach( $locale_def_files as $locale_def_file )
1115          {    // Check if it's a definition file:
1116              // pre_dump( $locale_def_file );
1117              if( preg_match( '~[a-z0-9\-.]\.locale\.php$~i', $locale_def_file ) )
1118              {    // We found a definition file:
1119                  // pre_dump( $locale_def_file );
1120                  include $ad_locale_folder.'/'.$locale_def_file;
1121              }
1122          }
1123      }
1124  
1125      // Copy any new locale definitions over to $locales:
1126      foreach( $locale_defs as $locale_code => $locale_def )
1127      {
1128          if( !isset( $locales[$locale_code] ) )
1129          {    // New locale, add to main array:
1130              $locales[$locale_code] = $locale_def;
1131              // ... but mark as not enabled!
1132              $locales[$locale_code]['enabled'] = 0;
1133          }
1134      }
1135  
1136  
1137      // Assign priorities:
1138  
1139      // Find highest used priority:
1140      $max_prio = 0;
1141      foreach( $locales as $locale )
1142      {
1143          if( isset($locale['priority']) && $locale['priority'] > $max_prio )
1144          {
1145              $max_prio = $locale['priority'];
1146          }
1147      }
1148  
1149      foreach( $locales as $lkey=>$locale )
1150      {
1151          if( !isset($locale['priority']) )
1152          {
1153              $locales[$lkey]['priority'] = ++$max_prio;
1154          }
1155      }
1156  
1157  }
1158  
1159  
1160  /**
1161   * Get a number of the messages in the .PO & .POT file
1162   *
1163   * @param string File name (messages.po)
1164   * @param boolean TRUE - to calc a percent of translated messages
1165   * @return integer Number of the messages
1166   */
1167  function locale_file_po_info( $po_file_name, $calc_percent_done = false )
1168  {
1169      $all = 0;
1170      $fuzzy = 0;
1171      $this_fuzzy = false;
1172      $untranslated = 0;
1173      $translated = 0;
1174      $status = '-';
1175      $matches = array();
1176  
1177      if( file_exists( $po_file_name ) )
1178      {
1179          $lines = file( $po_file_name );
1180          $lines[] = '';    // Adds a blank line at the end in order to ensure complete handling of the file
1181  
1182          foreach( $lines as $line )
1183          {
1184              if( trim( $line ) == '' )
1185              {    // Blank line, go back to base status:
1186                  if( $status == 't' )
1187                  {    // ** End of a translation ** :
1188                      if( $msgstr == '' )
1189                      {
1190                          $untranslated++;
1191                          // echo 'untranslated: ', $msgid, '<br />';
1192                      }
1193                      else
1194                      {
1195                          $translated++;
1196                      }
1197                      if( $msgid == '' && $this_fuzzy )
1198                      {    // It's OK if first line is fuzzy
1199                          $fuzzy--;
1200                      }
1201                      $msgid = '';
1202                      $msgstr = '';
1203                      $this_fuzzy = false;
1204                  }
1205                  $status = '-';
1206              }
1207              elseif( ( $status == '-' ) && preg_match( '#^msgid "(.*)"#', $line, $matches ) )
1208              {    // Encountered an original text
1209                  $status = 'o';
1210                  $msgid = $matches[1];
1211                  // echo 'original: "', $msgid, '"<br />';
1212                  $all++;
1213              }
1214              elseif( ( $status == 'o' ) && preg_match( '#^msgstr "(.*)"#', $line, $matches ) )
1215              {    // Encountered a translated text
1216                  $status = 't';
1217                  $msgstr = $matches[1];
1218                  // echo 'translated: "', $msgstr, '"<br />';
1219              }
1220              elseif( preg_match( '#^"(.*)"#', $line, $matches ) )
1221              {    // Encountered a followup line
1222                  if( $status == 'o' )
1223                      $msgid .= $matches[1];
1224                  elseif( $status == 't' )
1225                      $msgstr .= $matches[1];
1226              }
1227              elseif( strpos( $line,'#, fuzzy' ) === 0 )
1228              {
1229                  $this_fuzzy = true;
1230                  $fuzzy++;
1231              }
1232          }
1233      }
1234  
1235      $info = array(
1236              'all'          => $all,
1237              'fuzzy'        => $fuzzy,
1238              'translated'   => $translated,
1239              'untranslated' => $untranslated
1240          );
1241  
1242      if( $calc_percent_done )
1243      {
1244          $info['percent'] = locale_file_po_percent_done( $info );
1245      }
1246  
1247      return $info;
1248  }
1249  
1250  
1251  /**
1252   * Get a percent of translated messages in the .PO file
1253   *
1254   * @param array File info (see result of the function locale_file_po_info() )
1255   * @return integer Percent
1256   */
1257  function locale_file_po_percent_done( $po_file_info )
1258  {
1259      global $messages_pot_file_info;
1260  
1261      if( !isset( $messages_pot_file_info ) )
1262      {    // Initialize a file info for the main language file if it doesn't yet set
1263          global $locales_path;
1264          $messages_pot_file_info = locale_file_po_info( $locales_path.'messages.pot' );
1265      }
1266  
1267      $percent_done = ( $messages_pot_file_info['all'] > 0 ) ? round( ( $po_file_info['translated'] - $po_file_info['fuzzy'] / 2 ) / $messages_pot_file_info['all'] * 100 ) : 0;
1268  
1269      return $percent_done;
1270  }
1271  
1272  
1273  /**
1274   * Insert default locales into T_locales.
1275   */
1276  function locale_insert_default()
1277  {
1278      global $DB, $current_locale, $locales, $test_install_all_features;
1279  
1280      $activate_locales = array();
1281  
1282      if( isset( $test_install_all_features ) && $test_install_all_features )
1283      { // Activate also additional locales on install
1284          $activate_locales[] = 'en-US';
1285          $activate_locales[] = 'de-DE';
1286          $activate_locales[] = 'fr-FR';
1287          $activate_locales[] = 'ru-RU';
1288      }
1289  
1290      if( ! empty( $current_locale ) )
1291      { // Make sure the user sees his new system localized.
1292          $activate_locales[] = $current_locale;
1293      }
1294  
1295      $activate_locales = array_unique( $activate_locales );
1296  
1297      if( ! empty( $activate_locales ) )
1298      { // Insert locales into DB
1299          $insert_data = array();
1300          foreach( $activate_locales as $a_locale )
1301          {
1302              if( !isset( $locales[ $a_locale ] ) )
1303              { // Skip an incorrect locale
1304                  continue;
1305              }
1306  
1307              // Make sure default transliteration_map is set
1308              $transliteration_map = '';
1309              if( isset( $locales[ $a_locale ]['transliteration_map'] ) && is_array( $locales[ $a_locale ]['transliteration_map'] ) )
1310              {
1311                  $transliteration_map = base64_encode( serialize( $locales[ $a_locale ]['transliteration_map'] ) );
1312              }
1313  
1314              $insert_data[] = '( '.$DB->quote( $a_locale ).', '
1315                  .$DB->quote( $locales[ $a_locale ]['charset'] ).', '
1316                  .$DB->quote( $locales[ $a_locale ]['datefmt'] ).', '
1317                  .$DB->quote( $locales[ $a_locale ]['timefmt'] ).', '
1318                  .$DB->quote( $locales[ $a_locale ]['startofweek'] ).', '
1319                  .$DB->quote( $locales[ $a_locale ]['name'] ).', '
1320                  .$DB->quote( $locales[ $a_locale ]['messages'] ).', '
1321                  .$DB->quote( $locales[ $a_locale ]['priority'] ).', '
1322                  .$DB->quote( $transliteration_map ).', '
1323                  .'1 )';
1324          }
1325  
1326          $DB->query( 'INSERT INTO T_locales '
1327                       .'( loc_locale, loc_charset, loc_datefmt, loc_timefmt, '
1328                       .'loc_startofweek, loc_name, loc_messages, loc_priority, '
1329                       .'loc_transliteration_map, loc_enabled ) '
1330                       .'VALUES '.implode( ', ', $insert_data ) );
1331      }
1332  }
1333  
1334  ?>

title

Description

title

Description

title

Description

title

title

Body