Seditio Source
Root |
./othercms/b2evolution_7.2.3/inc/locales/_locale.funcs.php
<?php
/**
 * This file implements functions for handling locales and i18n.
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
 *
 * @package evocore
 *
 * @todo Make it a class / global object!
 *        - Provide (static) functions to extract .po files / generate _global.php files (single quoted strings!)
 */
if( !defined('EVO_CONFIG_LOADED') ) die( 'Please, do not access this page directly.' );


// DEBUG: (Turn switch on or off to log debug info for specified category)
$GLOBALS['debug_locale'] = true;


// LOCALIZATION:
if( isset( $use_l10n ) && $use_l10n )
{
// We are going to use localization:

    /**
     * TRANSLATE!
     *
     * Translate a text to the desired locale or to the current locale.
     *
     * @param string String to translate, '' to get language file info (as in gettext spec)
     * @param string locale to translate to, '' to use current locale
     * @param array Array containing the following keys (all optional):
     *              - 'ext_transarray': A reference to an alternate array
     *                                  to use for the caching of the
     *                                  translated strings or NULL to use
     *                                  the internal array.
     *              - 'alt_basedir': Alternate base directory to search
     *                               for translations, e. g. a plugin or
     *                               skin directory.
     *              - 'for_helper': (boolean) Is the translation for the b2evoHelper object?
     * @return string The translated string or the original string on error.
     *
     * @internal The last parameter/its 'alt_basedir' key is used by
     *           Plugin::T_() and Skin::T_().
     */
   
function T_( $string, $req_locale = '', $params = array() )
    {
       
/**
         * The translations keyed by locale.
         *
         * This array is only used if $params['ext_transarray'] === NULL.
         *
         * @var array
         * @static
         */
       
static $_trans = array();

        global
$current_locale, $locales, $locales_path, $plugins_path;
        global
$evo_charset, $Debuglog;

       
$params = array_merge( array(
                               
'ext_transarray' => NULL,
                               
'alt_basedir'    => '',
                               
'for_helper'     => false,
                               
'add_transarray' => NULL,
                                ),
$params );

        if( empty(
$req_locale ) )
        {
// By default we use the current locale
           
if( empty( $current_locale ) )
            {
// don't translate if we have no locale
               
return $string;
            }

           
$req_locale = $current_locale;
        }

        if( ! isset(
$locales[$req_locale]['messages'] ) )
        {
           
$Debuglog->add( 'No messages file path for locale. $locales["'
                   
.$req_locale.'"] is '.var_export( @$locales[$req_locale], true ), 'locale' );

            if( ! empty(
$evo_charset ) ) // this extra check is needed, because $evo_charset may not yet be determined.. :/
           
{
               
$string = convert_charset( $string, $evo_charset, 'iso-8859-1' );
            }
            return
$string;
        }

       
$messages = $locales[$req_locale]['messages'];

        if (
is_null( $params['ext_transarray'] ) )
        {    
// use our array
            //$Debuglog->add( 'Using internal array', 'locale' );
           
$trans = & $_trans;
        }
        else
        {    
// use external array:
            //$Debuglog->add( 'Using external array', 'locale' );
           
$trans = & $params['ext_transarray'];
        }

        if( ! isset(
$trans[ $messages ] ) )
        {
// Translations for current locale have not yet been loaded:
           
$Debuglog->add( 'We need to load a new translation file to translate: "'. $string.'"', 'locale' );

            if (
$params['alt_basedir'] != '' )
            {    
// Load the translation file from the alternative base dir:
                //$Debuglog->add( 'Using alternative basedir ['.$params['alt_basedir'].']', 'locale' );
               
$path = $params['alt_basedir'].'/locales/'.$messages.'/_global.php';
            }
            else
            {    
// Load our global translation file.
               
$path = $locales_path.$messages.'/_global.php';
            }

            if(
file_exists($path) && is_readable($path) )
            {
               
$Debuglog->add( 'T_: Loading file: '.$path, 'locale' );
                include_once
$path;
            }
            else
            {
               
$Debuglog->add( 'T_: Messages file does not exist or is not readable: '.$path, 'locale' );
            }
            if( ! isset(
$trans[ $messages ] ) )
            {
// Still not loaded... file doesn't exist, memorize that no translations are available
                // echo 'file not found!';
               
$trans[ $messages ] = array();
            }
            else
            {
                if( ! isset(
$trans[$messages]['__meta__']) )
                {
// Unknown/old messages format (< version 1):
                   
$Debuglog->add( 'Found deprecated messages format (no __meta__ info).', 'locale' );
                   
// Translate keys (e.g. 'foo\nbar') to real strings ("foo\nbar")
                    // Doing this here for all strings, is actually faster than doing it on key lookup (like it has been done before always)
                   
foreach($trans[$messages] as $k => $v)
                    {
                        if( (
$pos = strpos($k, '\\')) === false )
                        {
// fast-path-skip
                           
continue;
                        }
                       
// Replace string as done in the good old days:
                       
$new_k = str_replace( array('\n', '\r', '\t'), array("\n", "\r", "\t"), $k );
                        if(
$new_k != $k )
                        {
                           
$trans[$messages][$new_k] = $v;
                            unset(
$trans[$messages][$k]);
                        }
                    }
                }
            }
        }

        if( ! empty(
$params['add_transarray'] ) )
        {
            switch(
$params['add_transarray'] )
            {
                case
'backoffice':
                    static
$_backoffice_array = array();
                   
$add_transarray_var = 'trans_backoffice';
                    ${
$add_transarray_var} = & $_backoffice_array;
                   
$trans_file_name    = '_back-office.php';
                    break;

                case
'demo_contents':
                    static
$_demo_contents_array = array();
                   
$add_transarray_var    = 'trans_demo_contents';
                    ${
$add_transarray_var} = & $_demo_contents_array;
                   
$trans_file_name       = '_demo-contents.php';
                    break;

                default:
                   
debug_die( 'Invalid additional translation array.' );
            }

            if( ! isset( ${
$add_transarray_var}[ $messages ] ) )
            {
// Additional translations for current locale have not yet been loaded:
               
$Debuglog->add( 'We need to load additional translation file to translate: "'. $string.'"', 'locale' );

                if (
$params['alt_basedir'] != '' )
                {    
// Load the translation file from the alternative base dir:
                    //$Debuglog->add( 'Using alternative basedir ['.$params['alt_basedir'].']', 'locale' );
                   
$path = $params['alt_basedir'].'/locales/'.$messages.'/'.$trans_file_name;
                }
                else
                {    
// Load our additional translation file.
                   
$path = $locales_path.$messages.'/'.$trans_file_name;
                }

                if(
file_exists($path) && is_readable($path) )
                {
                   
$Debuglog->add( 'T_: Loading additional file: '.$path, 'locale' );
                    include_once
$path;
                }
                else
                {
                   
$Debuglog->add( 'T_: Messages file does not exist or is not readable: '.$path, 'locale' );
                }
                if( ! isset( ${
$add_transarray_var}[ $messages ] ) )
                {
// Still not loaded... file doesn't exist, memorize that no translations are available
                    // echo 'file not found!';
                   
${$add_transarray_var}[ $messages ] = array();
                }
                else
                {
                    if( ! isset( ${
$add_transarray_var}[$messages]['__meta__'] ) )
                    {
// Unknown/old messages format (< version 1):
                       
$Debuglog->add( 'Found deprecated messages format (no __meta__ info).', 'locale' );
                       
// Translate keys (e.g. 'foo\nbar') to real strings ("foo\nbar")
                        // Doing this here for all strings, is actually faster than doing it on key lookup (like it has been done before always)
                       
foreach( ${$add_transarray_var}[$messages] as $k => $v )
                        {
                            if( (
$pos = strpos($k, '\\')) === false )
                            {
// fast-path-skip
                               
continue;
                            }
                           
// Replace string as done in the good old days:
                           
$new_k = str_replace( array( '\n', '\r', '\t' ), array( "\n", "\r", "\t" ), $k );
                            if(
$new_k != $k )
                            {
                                ${
$add_transarray_var}[$messages][$new_k] = $v;
                                unset( ${
$add_transarray_var}[$messages][$k] );
                            }
                        }
                    }
                }
            }
        }

       
// sam2kb> b2evolution creates _global.php files with "\n" line breaks, and we must normalize newlines
        // in supplied string before trying to translate it. Otherwise strings won't match.
        // fp> TODO: this is not really satisfying in the long term. We need our own
        // parser that will extract T_() TS_() NT_() etc string and create a normalized potfile.
        // Actually it sgould create several potfiles. One for general use, one for admin, one for install, etc.
        // That way translators can concentrate on the most essential stuff first.
       
$search_string = str_replace( array("\r\n", "\r"), "\n", $string );

        if( ! empty(
$params['add_transarray'] ) && isset( ${$add_transarray_var}[ $messages ][ $search_string ] ) )
        {
// If the string has been translated:
            //$Debuglog->add( 'String ['.$string.'] found', 'locale' );
           
$r = ${$add_transarray_var}[ $messages ][ $search_string ];
            if( isset( ${
$add_transarray_var}[$messages]['__meta__']['charset']) )
            {
// new format: charset in meta data:
               
$messages_charset = ${$add_transarray_var}[$messages]['__meta__']['charset'];
            }
            else
            {
// old format.. extract charset from content type or fall back to setting from global locale definition:
               
$meta = ${$add_transarray_var}[$messages][''];
                if(
preg_match( '~^Content-Type: text/plain; charset=(.*);?$~m', $meta, $match ) )
                {
                   
$messages_charset = $match[1];
                }
                else
                {
                   
$messages_charset = $locales[$req_locale]['charset'];
                }
               
// Set it accordingly to new format.
               
${$add_transarray_var}[$messages]['__meta__']['charset'] = $messages_charset;
            }
        }
        elseif( isset(
$trans[ $messages ][ $search_string ] ) )
        {
// If the string has been translated:
            //$Debuglog->add( 'String ['.$string.'] found', 'locale' );
           
$r = $trans[ $messages ][ $search_string ];
            if( isset(
$trans[$messages]['__meta__']['charset']) )
            {
// new format: charset in meta data:
               
$messages_charset = $trans[$messages]['__meta__']['charset'];
            }
            else
            {
// old format.. extract charset from content type or fall back to setting from global locale definition:
               
$meta = $trans[$messages][''];
                if(
preg_match( '~^Content-Type: text/plain; charset=(.*);?$~m', $meta, $match ) )
                {
                   
$messages_charset = $match[1];
                }
                else
                {
                   
$messages_charset = $locales[$req_locale]['charset'];
                }
               
// Set it accordingly to new format.
               
$trans[$messages]['__meta__']['charset'] = $messages_charset;
            }
        }
        else
        {
           
//$Debuglog->add( 'String ['.$string.'] not found', 'locale' );
            // Return the English string:
           
$r = $string;

           
// $messages_charset = 'iso-8859-1'; // our .php file encoding
            // fp> I am changing the above for the User custom field group labels (in theroy the php files are plain ASCII anyways!!)
           
$messages_charset = $evo_charset;
        }

        if( ! empty(
$evo_charset) ) // this extra check is needed, because $evo_charset may not yet be determined.. :/
       
{
           
$r = convert_charset( $r, $evo_charset, $messages_charset );
        }
        else
        {
           
$Debuglog->add(sprintf('Warning: evo_charset not set to translate "%s"', htmlspecialchars($string)), 'locale');
        }

        if(
$params['for_helper'] )
        {
// translation is for the b2evoHelper object
           
add_js_translation( $string, $r );
        }

       
//$Debuglog->add( 'Result: ['.$r.']', 'locale' );
       
return $r;
    }

   
/**
     * Translate strings in Back-office UI.
     *
     * @param string String to translate
     * @return string The translated string
     */
   
function TB_( $string, $req_locale = '', $params = array() )
    {
       
$params = array_merge( array(
               
'add_transarray' => 'backoffice',
            ),
$params );

        return
T_( $string, $req_locale, $params );
    }


   
/**
     * Translate strings in demo/sample contents.
     *
     * @param string String to translate
     * @return string The translated string
     */
   
function TD_( $string, $req_locale = '', $params = array() )
    {
       
// TODO: Later we will make a specific .POT file containing ONLY demo contents.
       
$params = array_merge( array(
           
'add_transarray' => 'demo_contents',
        ),
$params );

        return
T_( $string, $req_locale, $params );
    }

}
else
{
// We are not localizing at all:

    /**
     * @ignore
     */
   
function T_( $string, $req_locale = '', $params = array() )
    {
        return
$string;
    }


   
/**
     * @ignore
     */
   
function TB_( $string, $req_locale = '', $params = array() )
    {
        return
$string;
    }


   
/**
     * @ignore
     */
   
function TD_( $string, $req_locale = '', $params = array() )
    {
        return
$string;
    }

}

/**
 * Translate and escape single quotes.
 *
 * This is to be used mainly for Javascript strings.
 *
 * @uses T_()
 * @param string String to translate
 * @param string Locale to use
 * @param array  See {@link T_()}
 * @return string The translated and escaped string.
 */
function TS_( $string, $req_locale = '', $params = array() )
{
    return
str_replace( "'", "\\'", T_( $string, $req_locale, $params ) );
}


/**
 * Temporarily switch to another locale
 *
 * Calls can be nested, see {@link locale_restore_previous()}.
 *
 * @param string locale to activate
 * @return boolean true on success, false on failure
 */
function locale_temp_switch( $locale )
{
    global
$saved_locales, $current_locale, $Timer;

   
// $Timer->resume( 'locale_temp_switch' );

   
if( !isset( $saved_locales ) || ! is_array( $saved_locales ) )
    {
       
$saved_locales = array();
    }

   
$prev_locale = $current_locale;
    if(
locale_activate( $locale ) )
    {
       
array_push( $saved_locales, $prev_locale );
        return
true;
    }

   
// $Timer->stop( 'locale_temp_switch' );
   
return false;
}


/**
 * Restore the locale in use before the switch
 *
 * @see locale_temp_switch()
 * @return boolean true on success, false on failure (no locale stored before)
 */
function locale_restore_previous()
{
    global
$saved_locales;

    if( !empty(
$saved_locales ) && is_array( $saved_locales ) )
    {
       
locale_activate( array_pop( $saved_locales ) );
        return
true;
    }
    return
false;
}


/**
 * Activate a locale.
 *
 * @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
 *
 * @param string locale to activate
 * @param boolean True on success/change, false on failure (if already set or not existant)
 */
function locale_activate( $locale )
{
    global
$locales, $current_locale, $current_charset;

    if(
$locale == $current_locale
           
|| empty( $locale )
            || ! isset(
$locales[$locale] ) )
    {
        return
false;
    }

   
// Memorize new locale:
   
$current_locale = $locale;
   
// Memorize new charset:
   
$current_charset = $locales[ $locale ][ 'charset' ];
    return
true;
}


/**
 * locale_by_lang(-)
 *
 * Find first locale matching lang
 */
function locale_by_lang( $lang, $fallback_to_default = true )
{
    global
$locales, $default_locale;

    foreach(
$locales as $locale => $locale_params )
    {
        if(
substr( $locale, 0 ,2 ) == $lang )
        {
// found first matching locale
           
return $locale;
        }
    }

   
// Not found...
   
if( $fallback_to_default )
        return
$default_locale;
    else
        return
$lang;
}


/**
 * Displays/Returns the current locale. (for backward compatibility)
 *
 * This is for HTML lang attributes
 *
 * @param boolean true (default) if we want it to be outputted
 * @return string current locale, if $disp = false
 */
function locale_lang( $disp = true )
{
    global
$current_locale;

    if(
$disp )
        echo
$current_locale;
    else
        return
$current_locale;
}


/**
 * Returns the charset of the current locale
 */
function locale_charset( $disp = true )
{
    global
$current_charset;

    if(
$disp )
        echo
$current_charset;
    else
        return
$current_charset;
}


/**
 * Resolves a #date_format_code to the correct date or time format
 *
 * @param string Locale, must be set in {@link $locales}
 * @return string Date format of the locale, e.g. 'd.m.Y'
 */
function locale_resolve_datetime_fmt( $format, $locale = NULL )
{
   
// Get datetime format:
   
switch( $format )
    {
        case
'#short_date':
        case
'':
           
$format = locale_datefmt( $locale );
            break;

        case
'#long_date':
           
$format = locale_longdatefmt( $locale );
            break;

        case
'#extended_date':
           
$format = locale_extdatefmt( $locale );
            break;

        case
'#short_time':
           
$format = locale_shorttimefmt( $locale );
            break;

        case
'#long_time':
           
$format = locale_timefmt( $locale );
            break;

        case
'#short_date_time':
           
$format = locale_datefmt( $locale ).' '.locale_shorttimefmt( $locale );
            break;
    }

    return
$format;
}


/**
 * Returns the current locale's default date format
 * @param string Locale, must be set in {@link $locales}
 * @return string Date format of the locale, e.g. 'd.m.Y'
 */
function locale_datefmt( $locale = NULL )
{
    return
locale_get( 'datefmt', $locale );
}


/**
 * Returns the current locale's default long date format
 * @param string Locale, must be set in {@link $locales}
 * @return string Date format of the locale, e.g. 'd.m.Y'
 */
function locale_longdatefmt( $locale = NULL )
{
    global
$locales;

    if( empty(
$locale) )
    {
        global
$current_locale;
       
$locale = $current_locale;
    }

    return
$locales[$locale]['longdatefmt'];
}


/**
 * Returns the current locale's default extended date format
 * @param string Locale, must be set in {@link $locales}
 * @return string Date format of the locale, e.g. 'd.m.Y'
 */
function locale_extdatefmt( $locale = NULL )
{
    global
$locales;

    if( empty(
$locale) )
    {
        global
$current_locale;
       
$locale = $current_locale;
    }

    return
$locales[$locale]['extdatefmt'];
}


/**
 * Returns the current locale's default input date format
 * @param string Locale, must be set in {@link $locales}
 * @return string Date format of the locale, e.g. 'd.m.Y'
 */
function locale_input_datefmt( $locale = NULL )
{
    global
$locales;

    if( empty(
$locale) )
    {
        global
$current_locale;
       
$locale = $current_locale;
    }

    return
$locales[$locale]['input_datefmt'];
}


/**
 * Returns the current locale's default time format
 */
function locale_timefmt()
{
    return
locale_get( 'timefmt' );
}

/**
 * Returns the current locale's default short time format
 */
function locale_shorttimefmt()
{
    return
locale_get( 'shorttimefmt' );
}


function
locale_datetimefmt( $separator = ' ' )
{
    return
locale_get( 'datefmt' ).$separator.locale_get( 'timefmt' );
}


/**
 * Returns the current locale's start of week
 *
 * @return integer 0 for Sunday, 1 for Monday
 */
function locale_startofweek()
{
    return ( int )
locale_get( 'startofweek' );
}


/**
 * Get the country locale
 *
 * @param string locale to use, '' for current
 *
 * @return string country locale
 */
function locale_country( $locale = '' )
{
    global
$current_locale;

    if( empty(
$locale) ) $locale = $current_locale;

    return
substr( $locale, 3, 2 );
}


/**
 *    Get the locale country dialing code
 */
function locale_dialing_code( $locale = '' )
{
        global
$current_locale, $CountryCache;

        if( empty(
$locale) )
        {
           
$locale = locale_country();
        }

       
$edited_Country = $CountryCache->get_by_ID( $locale);

        return
$edited_Country->dialing_code;
}


/**
 * Template function: Display locale flag
 *
 * @param string locale to use, '' for current
 * @param string DEPRECATED PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 * @param string name of class for IMG tag   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 * @param string deprecated HTML align attribute   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 * @param boolean to echo or not
 * @param mixed use absolute url (===true) or path to flags directory (used in installer)   !! OLD PARAM - NOT USED IN THE FUNCTION ANYMORE !!
 */
function locale_flag( $locale = '', $collection = 'deprecated_param', $class = 'flag', $align = '', $disp = true, $absoluteurl = true )
{
    global
$locales, $current_locale, $country_flags_bg;

    if( empty(
$locale ) )
    {
       
$locale = $current_locale;
    }

   
// extract flag name:
   
$country_code = strtolower( substr( $locale, 3, 2 ) );

   
$flag_attribs = array(
       
'class' => 'flag',
       
'title' => isset($locales[$locale]['name']) ? $locales[$locale]['name'] : $locale,
    );

    if( isset(
$country_flags_bg[ $country_code ] ) )
    {    
// Set background-position from config
       
$flag_attribs['style'] = 'background-position:'.$country_flags_bg[ $country_code ];
    }

   
$r = '<span'.get_field_attribs_as_string( $flag_attribs ).'></span>';

    if(
$disp )
        echo
$r;   // echo it
   
else
        return
$r; // return it
}


/**
 * [callback function] Outputs an <option> set with default locale selected.
 * Optionally returns an array with a locale key and name if there's only one enabled locale.
 *
 * @param string default value
 * @param boolean echo output?
 * @param boolean Return array (locale key + name) if there's only one enabled locale?
 * @return string|array The options string or an array (locale key + name) if there's only one enabled locale and $array_if_onelocale == true.
 */
function locale_options( $default = '', $disp = true, $array_if_onelocale = false )
{
    global
$locales, $default_locale;

    if( empty(
$default ) ) $default = $default_locale;

   
$r = '';
   
$enabled_count = 0;
   
$enabled_lastkey = '';

    foreach(
$locales as $this_localekey => $this_locale )
    {
        if(
$this_locale['enabled'] || $this_localekey == $default )
        {
           
$r .= '<option value="'. $this_localekey. '"';
            if(
$this_localekey == $default )
               
$r .= ' selected="selected"';
           
$r .= '>'. T_($this_locale['name']). '</option>';

            ++
$enabled_count;
           
$enabled_lastkey = $this_localekey;
        }
    }

    if(
$disp )
    {    
// the result must be displayed
       
echo $r;
    }

    if (
$array_if_onelocale && $enabled_count == 1 )
    {    
// We've only one enabled locale:
       
return array( $enabled_lastkey, $locales[$enabled_lastkey]['name'] );
    }
    else
    {    
// Return the string.
       
return $r;
    }
}


/**
 * [callback function] Returns an <option> set with default locale selected
 *
 * @param string default value
 */
function locale_options_return( $default = '' )
{
   
$r = locale_options( $default, false );
    return
$r;
}


/**
 * Detect language from HTTP_ACCEPT_LANGUAGE
 *
 * HTTP_ACCEPT_LANGUAGE is sorted by prio and then the best match is used
 * (either full locale ("en-US") or best fitting locale for a short one ("en").
 *
 * This gets tested in {@link test_locale_from_httpaccept()}.
 *
 * @author Rewritten by blueyed in Revision 1.42
 *
 * @return string Locale made out of HTTP_ACCEPT_LANGUAGE or $default_locale, if no match
 */
function locale_from_httpaccept()
{
    global
$locales, $default_locale;
    if( isset(
$_SERVER['HTTP_ACCEPT_LANGUAGE']) )
    {
       
$accept = strtolower( $_SERVER['HTTP_ACCEPT_LANGUAGE'] );

       
// Create list of accepted locales.
       
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) )
        {
            return
$default_locale;
        }
       
// Create list of enabled locales.
       
$enabled_locales = array();
        foreach(
$locales as $k => $v)
        {
            if( empty(
$v['enabled']) )
            {
                continue;
            }
           
$enabled_locales[strtolower($k)] = $k;
        }
        if( empty(
$enabled_locales) )
        {
            return
$default_locale;
        }
       
// Build mapping of short code to long code(s)
       
$short_locales = array();
        foreach(
$enabled_locales as $v)
        {
            if(
$pos = strpos($v, '-') )
            {
               
$short = substr($v, 0, $pos);
               
$short_locales[$short][] = $v;
            }
        }
       
// Create "locale" => "prio" list
       
$accept_list = array_combine($accept_list[1], $accept_list[2]);
       
$maxq = count($accept_list)+1;
        foreach(
$accept_list as $k => $v )
        {
            if(
$v === '' )
            {
// should be kept in order
               
$accept_list[$k] = $maxq--;
            }
            elseif(
$v == 0 )
            {
// not acceptable (RFC 2616)
               
unset($accept_list[$k]);
            }
        }
       
arsort($accept_list);
       
$accept_list = array_values(array_keys($accept_list));

       
// Go through the list of accepted locales and find best match.
       
for( $i = 0, $n = count($accept_list); $i<$n; $i++ )
        {
           
$test = $accept_list[$i];

            if( isset(
$enabled_locales[$test]) )
            {
// the accepted locale is enabled and a full match
               
return $enabled_locales[$test];
            }
            if( isset(
$short_locales[$test]) )
            {
// this is a short locale: find the best/first match in accepted locales
               
$first = NULL;
                foreach(
$short_locales[$test] as $v)
                {
                   
$pos = array_search(strtolower($v), $accept_list);
                    if(
$pos !== false && ( ! isset($first) || $pos < $first ) )
                    {
                       
$first = $pos;
                    }
                }
                if( isset(
$first) )
                {
// found exact match, via short locale.
                   
return $enabled_locales[$accept_list[$first]];
                }
                if( isset(
$enabled_locales[$test.'-'.$test]) )
                {
// test for e.g. "de-DE" when only "de" is accepted
                   
return $enabled_locales[$test.'-'.$test];
                }
               
// Fallback: use first enabled locale matching the short accepted
               
return $short_locales[$test][0];
            }
        }
    }
    return
$default_locale;
}


/**
 * user sort function to sort locales by priority
 *
 * 1 is highest priority.
 *
 */
function locale_priosort( $a, $b )
{
    return
$a['priority'] - $b['priority'];
}


/**
 * load locales from DB into $locales array. Also sets $default_locale.
 *
 * @return mixed new default locale on succes, false on failure
 */
function locale_overwritefromDB()
{
    global
$DB, $locales, $default_locale, $Settings, $Debuglog, $Messages;

   
// Load all locales from disk to get 'charset' insrtad of DB
   
locales_load_available_defs();

   
$usedprios = array();  // remember which priorities are used already.
   
$priocounter = 0;
   
$query = 'SELECT
                        loc_locale, loc_datefmt, loc_longdatefmt, loc_extdatefmt, loc_input_datefmt, loc_timefmt, loc_shorttimefmt, loc_input_timefmt, loc_startofweek,
                        loc_name, loc_messages, loc_priority, loc_transliteration_map, loc_enabled
                        FROM T_locales ORDER BY loc_priority'
;

   
$ctrl = isset( $_GET['ctrl'] ) ? $_GET['ctrl'] : '';
   
$priocounter_max = 0;
   
$wrong_locales = array();
   
$wrong_default_locale = '';
    foreach(
$DB->get_results( $query, ARRAY_A ) as $row )
    {
// Loop through loaded locales:

       
if( $row['loc_priority'] == $priocounter )
        {
// priority conflict (the same)
           
$priocounter++;
        }
        else
        {
           
$priocounter = $row['loc_priority'];
        }
       
$priocounter_max = $priocounter;

       
//remember that we used this
       
$usedprios[] = $priocounter;

       
$transliteration_map = '';
       
// Try to unserialize the value
       
if( ( $r = @unserialize( @base64_decode( $row[ 'loc_transliteration_map' ] ) ) ) !== false )
        {
           
$transliteration_map = $r;
        }

        if( isset(
$locales[ $row['loc_locale'] ], $locales[ $row['loc_locale'] ]['charset'] ) )
        {
// Use charset from locale file if such file exists on disk
           
$loc_charset = $locales[ $row['loc_locale'] ]['charset'];
        }
        else
        {
// Use charset from DB
           
$loc_charset = 'utf-8';
        }

        if( (
$ctrl == 'locales' || $ctrl == 'regional' ) && $row['loc_enabled'] && $loc_charset != 'utf-8' )
        {
// Check wrong non utf-8 locales in order to deactivate them
           
$Messages->add( sprintf( T_('The locale %s has been deactivated because it\'s not UTF-8'), '<b>'.$row['loc_locale'].'</b>' ) );
            if(
$Settings->get( 'default_locale' ) == $row['loc_locale'] && $default_locale != $row['loc_locale'] )
            {
// Change main locale to default
               
$wrong_default_locale = $row['loc_locale'];
               
$Messages->add( sprintf( T_('The main locale was no longer valid and has been set to %s.'), '<b>'.$default_locale.'</b>' ) );
            }
           
$wrong_locales[] = $row['loc_locale'];
           
$row['loc_enabled'] = 0;
        }

       
$locales[ $row['loc_locale'] ] = array(
               
'charset'      => $loc_charset,
               
'datefmt'      => $row['loc_datefmt'],
               
'longdatefmt'  => $row['loc_longdatefmt'],
               
'extdatefmt'   => $row['loc_extdatefmt'],
               
'input_datefmt' => $row['loc_input_datefmt'],
               
'timefmt'      => $row['loc_timefmt'],
               
'shorttimefmt' => $row['loc_shorttimefmt'],
               
'input_timefmt' => $row['loc_input_timefmt'],
               
'startofweek'  => $row['loc_startofweek'],
               
'name'         => $row['loc_name'],
               
'messages'     => $row['loc_messages'],
               
'transliteration_map' => $transliteration_map,
               
'priority'     => $priocounter,
               
'enabled'      => $row['loc_enabled'],
               
'fromdb'       => 1
           
);
    }

    if(
count( $wrong_locales ) )
    {
// Deactivate locales that have wrong charset
       
$DB->query( 'UPDATE T_locales
              SET loc_enabled = 0
            WHERE loc_locale IN( '
.$DB->quote( $wrong_locales ).' )' );
        if( ! empty(
$wrong_default_locale ) )
        {
// If main locale is wring we sgould updated it to default and use this for all users and blogs that used this wrong locale
           
$Settings->set( 'default_locale', $default_locale );
           
$Settings->dbupdate();
           
$DB->query( 'UPDATE T_users
                  SET user_locale = '
.$DB->quote( $default_locale ).'
                WHERE user_locale = '
.$DB->quote( $wrong_default_locale ) );
           
$DB->query( 'UPDATE T_blogs
                  SET blog_locale = '
.$DB->quote( $default_locale ).'
                WHERE blog_locale = '
.$DB->quote( $wrong_default_locale ) );
        }
    }

    foreach(
$locales as $l_ley => $locale )
    {
// Change priority of non-db locales to put them at the end of list
       
if( empty( $locale['fromdb'] ) && $locale['priority'] < $priocounter_max )
        {
           
$locales[ $l_ley ]['priority'] = $priocounter_max + $locale['priority'];
        }
    }

   
// set default priorities, if nothing was set in DB.
    // Missing "priority gaps" will get filled here.
   
if( $DB->num_rows != count($locales) )
    {
// we have locales from conf file that need a priority
       
$priocounter = 1;
        foreach(
$locales as $lkey => $lval )
        {
// Loop through memory locales:
           
if( !isset($lval['priority']) )
            {
// Found one that has no assigned priority
               
while( in_array( $priocounter, $usedprios ) )
                {
                   
$priocounter++;
                }
               
// Priocounter has max value
               
$locales[$lkey]['priority'] = $priocounter;
               
$usedprios[] = $priocounter;
            }
        }
    }

   
// sort by priority
   
uasort( $locales, 'locale_priosort' );

   
// overwrite default_locale from DB settings - if enabled.
    // Checks also if previous $default_locale is enabled. Defaults to en-EU, even if not enabled.
   
$locale_fromdb = $Settings->get('default_locale');

    if(
$locale_fromdb )
    {
        if( !isset(
$locales[$locale_fromdb] ) )
        {
           
$Debuglog->add( 'Default locale ['.$locale_fromdb.'] from general settings is not available.', 'locale' );
            return
false;
        }
        else
        {
           
$default_locale = $locale_fromdb;
            return
$default_locale;
        }
    }
}


/**
 * Write $locales array to DB table
 */
function locale_updateDB()
{
    global
$locales, $DB, $Settings, $Messages, $action, $admin_url;
    global
$saved_params;

   
$templocales = $locales;
   
$disabled_locales = array();
   
$saved_params = array();

   
$current_default_locale = $Settings->get( 'default_locale' );

   
$lnr = 0;
   
// Loop through list of all HTTP POSTed params:
   
foreach( $_POST as $pkey => $pval )
    {
        if( !
preg_match( '/loc_(\d+)_(.*)/', $pkey, $matches ) )
        {
            continue;
        }

       
// This is a locale related parameter, get it now:
       
$pval = param( $pkey, 'string', '' );
       
$saved_params[ $pkey ] = $pval;

       
$lfield = $matches[2];

        if(
$matches[1] != $lnr )
        {
// we have a new locale
           
$lnr = $matches[1];
           
$plocale = $pval;

            if(
$templocales[ $plocale ]['enabled'] || ( $plocale == $current_default_locale && ! isset( $_POST['loc_'.$lnr.'_enabled'] ) ) )
            {    
// Collect previously enabled locales AND if current default locale is not enabled yet:
               
$disabled_locales[ $plocale ] = $plocale;
            }
           
// checkboxes default to 0
           
$templocales[ $plocale ]['enabled'] = 0;
        }
        elseif(
$lnr != 0 )  // be sure to have catched a locale before
       
{
            if(
$lfield == 'datefmt' && empty( $pval ) )
            {
               
param_error( $pkey, T_('Date format cannot be empty.').' ('.$plocale.')' );
            }
            elseif(
$lfield == 'input_datefmt' && empty( $pval ) )
            {
               
param_error( $pkey, T_('Input date format cannot be empty.').' ('.$plocale.')' );
            }
            elseif(
$lfield == 'timefmt' && empty( $pval ) )
            {
               
param_error( $pkey, T_('Time format cannot be empty.').' ('.$plocale.')' );
            }
            elseif(
$lfield == 'input_timefmt' && empty( $pval ) )
            {
               
param_error( $pkey, T_('Input time format cannot be empty.').' ('.$plocale.')' );
            }

            if(
$lfield == 'startofweek' && ( $pval < 0 || $pval > 6 ) )
            {
// startofweek must be between 0 and 6
               
continue;
            }
            if(
$lfield == 'enabled' )
            {
// This locale is enabled, remove from the list of the disabled locales
               
unset( $disabled_locales[ $plocale ] );
            }
            elseif(
$plocale == $current_default_locale && isset( $disabled_locales[ $plocale ] ) && ! isset( $_POST['loc_'.$lnr.'_enabled'] ) )
            {
// The default locale must be always enabled
               
unset( $disabled_locales[ $plocale ] );
               
// Force the default locale to be enabled
               
$templocales[ $plocale ]['enabled'] = 1;
               
// Inform user about this action
               
$Messages->add( sprintf( T_('The locale %s was re-enabled automatically because it\'s currently the default locale. The default locale must be a valid and enabled locale.'), '<b>'.$current_default_locale.'</b>' ), 'warning' );
            }
           
$templocales[ $plocale ][ $lfield ] = $pval;
        }
    }

    if(
$Messages->has_errors() )
    {
        return
false;
    }

   
$locales = $templocales;

   
// Check the usage of the disabled locales
   
$disabled_locales = ( count( $disabled_locales ) > 0 ) ? $DB->quote( $disabled_locales ) : false;
    if(
$disabled_locales && ( $action != 'confirm_update' ) )
    {
// Some locales were disabled, create warning message if required
       
global $warning_message;
       
$warning_message = '';

       
// Display what MAIN collection locales will be disabled:
       
$SQL = new SQL( 'Get collections with disabled main locales' );
       
$SQL->SELECT( 'blog_locale, GROUP_CONCAT( blog_ID SEPARATOR "," ) ' );
       
$SQL->FROM( 'T_blogs' );
       
$SQL->WHERE( 'blog_locale IN ( '.$disabled_locales.' )' );
       
$SQL->ORDER_BY( 'blog_locale' );
       
$SQL->GROUP_BY( 'blog_locale' );
       
$SQL->HAVING( 'COUNT( blog_ID ) > 0' );
       
$main_locale_colls = $DB->get_assoc( $SQL );
        if(
count( $main_locale_colls ) > 0 )
        {
           
$BlogCache = & get_BlogCache();
           
$BlogCache->load_list( explode( ',', implode( ',', $main_locale_colls ) ) );
            foreach(
$main_locale_colls as $main_locale => $main_locale_coll_IDs )
            {
               
$warning_message .= sprintf( T_('The locale %s is used as main locale by the following collections:'), '<code>'.$main_locale.'</code>' ).'<ul style="list-style:disc;margin-left:20px">';
               
$main_locale_coll_IDs = explode( ',', $main_locale_coll_IDs );
                foreach(
$main_locale_coll_IDs as $main_locale_coll_ID )
                {
                   
$locale_Blog = & $BlogCache->get_by_ID( $main_locale_coll_ID );
                   
$coll_url = check_user_perm( 'blog_properties', 'edit', false, $main_locale_coll_ID )
                        ?
$admin_url.'?ctrl=coll_settings&amp;tab=general&amp;blog='.$locale_Blog->ID.'#fieldset_wrapper_language'
                       
: $locale_Blog->get( 'url' );
                   
$warning_message .= '<li><a href="'.$coll_url.'">'.$locale_Blog->get( 'name' ).'</a></li>';
                }
               
$warning_message .= '</ul>'.sprintf( T_('These will be switched to the default locale %s.'), '<code>'.$main_locale.'</code>' ).'<br><br>';
            }
        }

       
// Display what EXTRA collection locales will be disabled:
       
$SQL = new SQL( 'Get collections with disabled extra locales' );
       
$SQL->SELECT( 'cl_locale, GROUP_CONCAT( cl_coll_ID SEPARATOR "," ) ' );
       
$SQL->FROM( 'T_coll_locales' );
       
$SQL->WHERE( 'cl_locale IN ( '.$disabled_locales.' )' );
       
$SQL->ORDER_BY( 'cl_locale' );
       
$SQL->GROUP_BY( 'cl_locale' );
       
$SQL->HAVING( 'COUNT( cl_coll_ID ) > 0' );
       
$extra_locale_colls = $DB->get_assoc( $SQL );
        if(
count( $extra_locale_colls ) > 0 )
        {
           
$BlogCache = & get_BlogCache();
           
$BlogCache->load_list( explode( ',', implode( ',', $extra_locale_colls ) ) );
            foreach(
$extra_locale_colls as $extra_locale => $extra_locale_coll_IDs )
            {
               
$warning_message .= sprintf( T_('The locale %s is also used as extra locale by the following collections:'), '<code>'.$extra_locale.'</code>' ).'<ul style="list-style:disc;margin-left:20px">';
               
$extra_locale_coll_IDs = explode( ',', $extra_locale_coll_IDs );
                foreach(
$extra_locale_coll_IDs as $extra_locale_coll_ID )
                {
                   
$locale_Blog = & $BlogCache->get_by_ID( $extra_locale_coll_ID );
                   
$coll_url = check_user_perm( 'blog_properties', 'edit', false, $extra_locale_coll_ID )
                        ?
$admin_url.'?ctrl=coll_settings&amp;tab=general&amp;blog='.$locale_Blog->ID.'#fieldset_wrapper_language'
                       
: $locale_Blog->get( 'url' );
                   
$warning_message .= '<li><a href="'.$coll_url.'">'.$locale_Blog->get( 'name' ).'</a></li>';
                }
               
$warning_message .= '</ul>'.sprintf( T_('These collections will no longer use the %s extra locale.'), '<code>'.$extra_locale.'</code>' ).'<br><br>';
            }
        }

       
// Display what USER locales will be disabled:
       
$SQL = new SQL( 'Get users with disabled locales' );
       
$SQL->SELECT( 'user_locale, GROUP_CONCAT( user_ID SEPARATOR "," ) ' );
       
$SQL->FROM( 'T_users' );
       
$SQL->WHERE( 'user_locale IN ( '.$disabled_locales.' )' );
       
$SQL->ORDER_BY( 'user_locale' );
       
$SQL->GROUP_BY( 'user_locale' );
       
$SQL->HAVING( 'COUNT( user_ID ) > 0' );
       
$locale_users = $DB->get_assoc( $SQL );
        if(
count( $locale_users ) > 0 )
        {
           
$UserCache = & get_UserCache();
           
$UserCache->load_list( explode( ',', implode( ',', $locale_users ) ) );
            foreach(
$locale_users as $user_locale => $locale_user_IDs )
            {
               
$warning_message .= sprintf( T_('The locale %s is also used as preferred locale by the following users:'), '<code>'.$user_locale.'</code>' ).'<ul style="list-style:disc;margin-left:20px">';
               
$locale_user_IDs = explode( ',', $locale_user_IDs );
                foreach(
$locale_user_IDs as $locale_user_ID )
                {
                   
$locale_User = & $UserCache->get_by_ID( $locale_user_ID );
                   
$warning_message .= '<li>'.$locale_User->get_identity_link( array( 'user_tab' => 'userprefs' ) ).'</li>';
                }
               
$warning_message .= '</ul>'.sprintf( T_('These users\' preferred locale will fall back to %s.'), '<code>'.$user_locale.'</code>' ).'<br><br>';
            }
        }

        if( ! empty(
$warning_message ) )
        {
            return
false;
        }
    }

   
$query = "REPLACE INTO T_locales ( loc_locale, loc_datefmt, loc_longdatefmt, loc_extdatefmt, loc_input_datefmt,
            loc_timefmt, loc_shorttimefmt, loc_input_timefmt, loc_startofweek, loc_name, loc_messages, loc_priority,
            loc_transliteration_map, loc_enabled ) VALUES "
;
    foreach(
$locales as $localekey => $lval )
    {
        if( empty(
$lval['messages']) )
        {
// if not explicit messages file is given we'll translate the locale
           
$lval['messages'] = strtr($localekey, '-', '_');
        }

       
$transliteration_map = '';
        if( !empty(
$lval['transliteration_map']) )
        {
            if(
is_string($lval['transliteration_map']) )
            {    
// The value is already serialized and encoded
               
$transliteration_map = $lval['transliteration_map'];
            }
            else
            {    
// Encode the value
               
$transliteration_map = base64_encode( serialize($lval['transliteration_map']) );
            }
        }

       
$query .= '(
            '
.$DB->quote($localekey).',
            '
.$DB->quote($lval['datefmt']).',
            '
.$DB->quote($lval['longdatefmt']).',
            '
.$DB->quote($lval['extdatefmt']).',
            '
.$DB->quote($lval['input_datefmt']).',
            '
.$DB->quote($lval['timefmt']).',
            '
.$DB->quote($lval['shorttimefmt']).',
            '
.$DB->quote($lval['input_timefmt']).',
            '
.$DB->quote($lval['startofweek']).',
            '
.$DB->quote($lval['name']).',
            '
.$DB->quote($lval['messages']).',
            '
.$DB->quote($lval['priority']).',
            '
.$DB->quote($transliteration_map).',
            '
.$DB->quote($lval['enabled']).'
        ), '
;
    }
   
$query = substr($query, 0, -2);

   
$DB->begin();

   
$result = true;
    if(
$action == 'confirm_update' )
    {
// Update users and blogs locale to the default if the prevously used locale was disabled
       
$users_update = $DB->query( 'UPDATE T_users
              SET user_locale = '
.$DB->quote( $current_default_locale ).'
            WHERE  user_locale IN ( '
.$disabled_locales.' )' );
       
$colls_main_locales_update = $DB->query( 'UPDATE T_blogs
              SET blog_locale = '
.$DB->quote( $current_default_locale ).'
            WHERE blog_locale IN ( '
.$disabled_locales.' )' );
       
$colls_extra_locales_update = $DB->query( 'DELETE FROM T_coll_locales
            WHERE cl_locale IN ( '
.$disabled_locales.' )' );
       
$result = ( $users_update !== false ) && ( $colls_main_locales_update !== false ) && ( $colls_extra_locales_update !== false );
    }

    if(
$result && ( $DB->query($query) !== false ) )
    {
// Commit after successful update
       
$DB->commit();
        return
true;
    }

   
// Some error occured, rollback the transaction and add error message
   
$DB->rollback();
   
$Messages->add( T_('Unexpected error occured, regional settings could not be updated.') );
    return
false;
}


/**
 * Convert a string from one charset to another.
 *
 * @todo Implement iconv and PHP mapping tables
 *
 * @see can_convert_charsets()
 * @param string String to convert
 * @param string Target charset (TO)
 * @param string Source charset (FROM)
 * @return string Encoded string (if it cannot be converted it's the original one)
 */
function convert_charset( $string, $dest_charset, $src_charset )
{
    if( isset(
$GLOBALS['Timer'] ) )
    {
       
$GLOBALS['Timer']->resume( 'convert_charset', false );
    }

    if(
$dest_charset == $src_charset || $dest_charset == '' /* may happen if $evo_charset is not defined yet */ )
    {    
// no conversation required
       
if( isset( $GLOBALS['Timer'] ) )
        {
           
$GLOBALS['Timer']->pause( 'convert_charset', false );
        }
        return
$string;
    }

    if(
function_exists( 'mb_convert_variables' ) )
    {    
// Convert by mbstring extension if it is enabled:
       
mb_convert_variables( $dest_charset, $src_charset, $string );
    }
    elseif(
function_exists( 'iconv' ) )
    {    
// Convert by iconv extension it it is enabled:
       
$string = iconv( $src_charset, $dest_charset, $string );
    }

    if( isset(
$GLOBALS['Timer'] ) )
    {
       
$GLOBALS['Timer']->pause( 'convert_charset', false );
    }

    return
$string;
}


/**
 * Can we convert from charset A to charset B?
 * @param string Target charset (TO)
 * @param string Source charset (FROM)
 * @return boolean
 */
function can_convert_charsets( $dest_charset, $src_charset )
{
    if( empty(
$dest_charset) || empty($src_charset) )
    {
        return
false;
    }

    if(
function_exists('mb_internal_encoding') )
    {
// mb_string extension:
       
$orig = mb_internal_encoding();

       
$r = false;
        if( @
mb_internal_encoding($dest_charset) && @mb_internal_encoding($src_charset) )
        {
// we can set both encodings, so we should be able to convert:
           
$r = true;
        }

       
mb_internal_encoding($orig);
        return
$r;
    }

    return
false;
}


/**
 * Can we check for valid encodings of strings, using {@link check_encoding()}?
 *
 * @return boolean
 */
function can_check_encoding()
{
    return
function_exists('mb_check_encoding');
}


/**
 * Check if the string is valid for the specified encoding.
 *
 * @param string String to check
 * @param string Encoding to check
 * @return
 */
function check_encoding($str, $encoding)
{
    if(
function_exists('mb_check_encoding') )
    {
        return
mb_check_encoding($str, $encoding);
    }

    return
NULL;
}


/**
 * Init charset handling between Input/Output ($io_charset) and the internal
 * handling ($evo_charset).
 *
 * Check and possibly adjust {@link $evo_charset}.
 *
 * @staticvar boolean Used to only start mb_output_handler once
 * @param string I/O (input/output) charset to use
 * @return boolean true, if encoding has been changed
 */
function init_charsets( $req_io_charset )
{
    static
$mb_output_handler_started;
    global
$io_charset, $evo_charset, $Debuglog, $DB;
    global
$force_io_charset_if_accepted;

    if(
$req_io_charset == $io_charset )
    {
// no conversation/init needed
       
return false;
    }

   
// check, if we want to force a specific charset (e.g. 'utf-8'):
   
if( ! empty($force_io_charset_if_accepted) )
    {
// we want to force a specific charset:
       
if( ! isset($_SERVER['HTTP_ACCEPT_CHARSET']) // all allowed
           
|| preg_match( '~\b(\*|'.preg_quote( $force_io_charset_if_accepted, '~' ).')\b~', $_SERVER['HTTP_ACCEPT_CHARSET'] ) )
        {
           
$req_io_charset = $force_io_charset_if_accepted; // pretend that the first one has been requested
       
}
    }

    if(
$req_io_charset == $io_charset )
    {
// no conversation/init needed
       
return false;
    }

   
$io_charset = $req_io_charset;

    if( empty(
$evo_charset) )
    {
// empty evo_charset follows I/O charset:
        // TODO: $evo_charset will not follow, if it has followed before.. (because not empty anymore)
       
$Debuglog->add( '$evo_charset follows $io_charset ('.$io_charset.').', array('locale') );
       
$evo_charset = $io_charset;
    }
    elseif(
$evo_charset != $io_charset )
    {
// we have to convert for I/O
        // TODO: dh> $io_charset has to forcefully follow $evo_charset, if we cannot convert, e.g. utf-8/iso-8859-1
       
if( ! function_exists('mb_convert_encoding') )
        {
           
$Debuglog->add( '$evo_charset differs from $io_charset, but mbstrings is not available - cannot convert I/O to internal charset!', array('errors','locale') );
           
$evo_charset = $io_charset; // we cannot convert I/O to internal charset
       
}
        else
        {
// check if the encodings are supported:
            // NOTE: mb_internal_encoding() is the best way to find out if the encoding is supported
           
$old_mb_internal_encoding = mb_internal_encoding();
            if( ! @
mb_internal_encoding($io_charset) )
            {
               
$Debuglog->add( 'Cannot I/O convert because I/O charset ['.$io_charset.'] is not supported by mbstring!', array('error','locale') );
               
$evo_charset = $io_charset;
               
mb_internal_encoding($old_mb_internal_encoding);
            }
            elseif( ! @
mb_internal_encoding($evo_charset) )
            {
               
$Debuglog->add( 'Cannot I/O convert because $evo_charset='.$evo_charset.' is not supported by mbstring!', array('error','locale') );
               
$evo_charset = $io_charset;
               
mb_internal_encoding($old_mb_internal_encoding);
            }
            else
            {
// we can convert between I/O
               
mb_http_output( $io_charset );
                if( !
$mb_output_handler_started )
                {
                   
ob_start( 'mb_output_handler' ); // NOTE: this will send a Content-Type header by itself for "text/..."
                   
$mb_output_handler_started = true;
                   
$Debuglog->add( 'Started mb_output_handler.', 'locale' );
                }
            }
        }
    }

   
// Make sure the DB send us text in the same charset as $evo_charset (override whatever may have been set before)
   
if( isset($DB) ) // not available in /install/index.php
   
{    // Set encoding for MySQL connection:
       
$DB->set_connection_charset( $evo_charset );
    }

   
$Debuglog->add( 'evo_charset: '.$evo_charset, 'locale' );
   
$Debuglog->add( 'io_charset: '.$io_charset, 'locale' );

    return
true;
}


/**
 * Load available locale definitions
 */
function locales_load_available_defs()
{
    global
$locales_path, $locales, $locales_loaded_form_disk, $Messages;

    if( ! empty(
$locales_loaded_form_disk ) )
    {
// All locales have been already loaded
       
return;
    }

   
// This is where language packs will store their locale defintions:
   
$locale_defs = array();

   
// Get all locale folder names:
   
$filename_params = array(
           
'inc_files' => false,
           
'recurse'   => false,
           
'basename'  => true,
        );
   
$locale_folders = get_filenames( $locales_path, $filename_params );
   
// Go through all locale folders:
   
foreach( $locale_folders as $locale_folder )
    {
       
//pre_dump( $locale_folder );
       
$ad_locale_folder = $locales_path.'/'.$locale_folder;
       
// Get files in folder:
       
$filename_params = array(
               
'inc_dirs'    => false,
               
'recurse'    => false,
               
'basename'    => true,
            );
       
$locale_def_files = get_filenames( $ad_locale_folder, $filename_params );
        if(
$locale_def_files === false )
        {    
// Impossible to read the locale folder:
           
$Messages->add( sprintf( /* No trans for debug messages */ 'The locale %s is not readable. Please check file permissions.', $locale_folder ), 'warning' );
            continue;
        }
       
// Go through files in locale folder:
       
foreach( $locale_def_files as $locale_def_file )
        {    
// Check if it's a definition file:
            // pre_dump( $locale_def_file );
           
if( preg_match( '~[a-z0-9\-.]\.locale\.php$~i', $locale_def_file ) )
            {    
// We found a definition file:
                // pre_dump( $locale_def_file );
               
include $ad_locale_folder.'/'.$locale_def_file;
            }
        }
    }

   
// Copy any new locale definitions over to $locales:
   
foreach( $locale_defs as $locale_code => $locale_def )
    {
        if( !isset(
$locales[$locale_code] ) )
        {    
// New locale, add to main array:
           
$locales[$locale_code] = $locale_def;
           
// ... but mark as not enabled!
           
$locales[$locale_code]['enabled'] = 0;
        }
    }


   
// Assign priorities:

    // Find highest used priority:
   
$max_prio = 0;
    foreach(
$locales as $locale )
    {
        if( isset(
$locale['priority']) && $locale['priority'] > $max_prio )
        {
           
$max_prio = $locale['priority'];
        }
    }

    foreach(
$locales as $lkey => $locale )
    {
        if( !isset(
$locale['priority']) )
        {
           
$locales[$lkey]['priority'] = ++$max_prio;
        }
    }

   
// Set this var to TRUE to don't call this function twice
   
$locales_loaded_form_disk = true;
}


/**
 * Get a number of the messages in the .PO & .POT file
 *
 * @param string File name (messages.po)
 * @param boolean TRUE - to calc a percent of translated messages
 * @return integer Number of the messages
 */
function locale_file_po_info( $po_file_name, $calc_percent_done = false )
{
   
$all = 0;
   
$fuzzy = 0;
   
$this_fuzzy = false;
   
$untranslated = 0;
   
$translated = 0;
   
$status = '-';
   
$matches = array();

    if(
file_exists( $po_file_name ) )
    {
       
$lines = file( $po_file_name );
       
$lines[] = '';    // Adds a blank line at the end in order to ensure complete handling of the file

       
foreach( $lines as $line )
        {
            if(
trim( $line ) == '' )
            {    
// Blank line, go back to base status:
               
if( $status == 't' )
                {    
// ** End of a translation ** :
                   
if( $msgstr == '' )
                    {
                       
$untranslated++;
                       
// echo 'untranslated: ', $msgid, '<br />';
                   
}
                    else
                    {
                       
$translated++;
                    }
                    if(
$msgid == '' && $this_fuzzy )
                    {    
// It's OK if first line is fuzzy
                       
$fuzzy--;
                    }
                   
$msgid = '';
                   
$msgstr = '';
                   
$this_fuzzy = false;
                }
               
$status = '-';
            }
            elseif( (
$status == '-' ) && preg_match( '#^msgid "(.*)"#', $line, $matches ) )
            {    
// Encountered an original text
               
$status = 'o';
               
$msgid = $matches[1];
               
// echo 'original: "', $msgid, '"<br />';
               
$all++;
            }
            elseif( (
$status == 'o' ) && preg_match( '#^msgstr "(.*)"#', $line, $matches ) )
            {    
// Encountered a translated text
               
$status = 't';
               
$msgstr = $matches[1];
               
// echo 'translated: "', $msgstr, '"<br />';
           
}
            elseif(
preg_match( '#^"(.*)"#', $line, $matches ) )
            {    
// Encountered a followup line
               
if( $status == 'o' )
                   
$msgid .= $matches[1];
                elseif(
$status == 't' )
                   
$msgstr .= $matches[1];
            }
            elseif(
strpos( $line,'#, fuzzy' ) === 0 )
            {
               
$this_fuzzy = true;
               
$fuzzy++;
            }
        }
    }

   
$info = array(
           
'all'          => $all,
           
'fuzzy'        => $fuzzy,
           
'translated'   => $translated,
           
'untranslated' => $untranslated
       
);

    if(
$calc_percent_done )
    {
       
$po_path_info    = pathinfo( $po_file_name );
       
$pot_file_name   = $po_path_info['filename'].'.pot';
       
$info['percent'] = locale_file_po_percent_done( $info, $pot_file_name );
    }

    return
$info;
}


/**
 * Get a percent of translated messages in the .PO file
 *
 * @param array File info (see result of the function locale_file_po_info() )
 * @return integer Percent
 */
function locale_file_po_percent_done( $po_file_info, $pot_file )
{
    global
$messages_pot_file_info;

    if( !isset(
$messages_pot_file_info[$pot_file] ) )
    {    
// Initialize a file info for the main language file if it doesn't yet set
       
global $locales_path;
       
$messages_pot_file_info[$pot_file] = locale_file_po_info( $locales_path.$pot_file );
    }

   
$percent_done = ( $messages_pot_file_info[$pot_file]['all'] > 0 ) ? round( ( $po_file_info['translated'] - $po_file_info['fuzzy'] / 2 ) / $messages_pot_file_info[$pot_file]['all'] * 100 ) : 0;

    return
$percent_done;
}


/**
 * Insert default locales into T_locales.
 */
function locale_insert_default()
{
    global
$DB, $current_locale, $locales, $install_test_features;

   
$activate_locales = array();

    if( ! empty(
$install_test_features ) )
    {    
// Activate also additional locales on install:
       
$activate_locales[] = 'en-US';
       
$activate_locales[] = 'de-DE';
       
$activate_locales[] = 'fr-FR';
       
$activate_locales[] = 'ru-RU';
    }

    if( ! empty(
$current_locale ) )
    {
// Make sure the user sees his new system localized.
       
$activate_locales[] = $current_locale;
    }

   
$activate_locales = array_unique( $activate_locales );

    if( ! empty(
$activate_locales ) )
    {
// Insert locales into DB
       
$SQL = new SQL( 'Get locales' );
       
$SQL->SELECT( 'loc_locale' );
       
$SQL->FROM( 'T_locales' );
       
$existing_locales = $DB->get_col( $SQL );

       
$insert_data = array();
        foreach(
$activate_locales as $a_locale )
        {
            if( !isset(
$locales[ $a_locale ] ) || in_array( $a_locale, $existing_locales ) )
            {
// Skip an incorrect locale or it already exists in DB
               
continue;
            }

           
// Make sure default transliteration_map is set
           
$transliteration_map = '';
            if( isset(
$locales[ $a_locale ]['transliteration_map'] ) && is_array( $locales[ $a_locale ]['transliteration_map'] ) )
            {
               
$transliteration_map = base64_encode( serialize( $locales[ $a_locale ]['transliteration_map'] ) );
            }

           
$insert_data[] = '( '.$DB->quote( $a_locale ).', '
               
.$DB->quote( $locales[ $a_locale ]['datefmt'] ).', '
               
.$DB->quote( empty( $locales[ $a_locale ]['longdatefmt'] ) ? str_replace( 'y', 'Y', $locales[ $a_locale ]['datefmt'] ) : $locales[ $a_locale ]['longdatefmt'] ).', '
               
.$DB->quote( empty( $locales[ $a_locale ]['extdatefmt'] ) ? str_replace( array( '.', ',', '\\', '/' ), ' ', str_replace( array( 'y', 'm' ), array( 'Y', 'M' ), $locales[ $a_locale ]['datefmt'] ) ) : $locales[ $a_locale ]['extdatefmt'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['input_datefmt'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['timefmt'] ).', '
               
.$DB->quote( empty( $locales[ $a_locale ]['shorttimefmt'] ) ? str_replace( ':s', '', $locales[ $a_locale ]['timefmt'] ) : $locales[ $a_locale ]['shorttimefmt'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['input_timefmt'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['startofweek'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['name'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['messages'] ).', '
               
.$DB->quote( $locales[ $a_locale ]['priority'] ).', '
               
.$DB->quote( $transliteration_map ).', '
               
.'1 )';
        }

        if(
count( $insert_data ) )
        {
// Do insert only if at least one locale requires this
           
$DB->query( 'INSERT INTO T_locales '
                     
.'( loc_locale, loc_datefmt, loc_longdatefmt, loc_extdatefmt, loc_input_datefmt, loc_timefmt, loc_shorttimefmt, loc_input_timefmt, '
                     
.'loc_startofweek, loc_name, loc_messages, loc_priority, '
                     
.'loc_transliteration_map, loc_enabled ) '
                     
.'VALUES '.implode( ', ', $insert_data ) );
        }
    }
}


/**
 * Restore default values of locales
 *
 * @param array Locale keys/codes
 */
function locale_restore_defaults( $restored_locales )
{
    global
$DB, $locales;

    if( empty(
$restored_locales ) && ! is_array( $restored_locales ) )
    {    
// No locales to restore, Exit here:
       
return;
    }

    foreach(
$restored_locales as $restored_locale_key )
    {
        if( ! isset(
$locales[ $restored_locale_key ] ) )
        {    
// Skip an incorrect locale:
           
continue;
        }

       
$restored_locale = $locales[ $restored_locale_key ];

       
// Make sure default transliteration_map is set:
       
$transliteration_map = '';
        if( isset(
$restored_locale['transliteration_map'] ) && is_array( $restored_locale['transliteration_map'] ) )
        {
           
$transliteration_map = base64_encode( serialize( $restored_locale['transliteration_map'] ) );
        }

       
// Restore all locale fields to default values:
       
$DB->query( 'UPDATE T_locales SET
            loc_datefmt             = '
.$DB->quote( $restored_locale['datefmt'] ).',
            loc_longdatefmt         = '
.$DB->quote( empty( $restored_locale['longdatefmt'] ) ? str_replace( 'y', 'Y', $restored_locale['datefmt'] ) : $restored_locale['longdatefmt'] ).',
            loc_extdatefmt          = '
.$DB->quote( empty( $restored_locale['extdatefmt'] ) ? str_replace( array( '.', '-', '\\', '/'), ' ', str_replace( array( 'y', 'm' ), array( 'Y', 'M' ), $restored_locale['datefmt'] ) ) : $restored_locale['extdatefmt'] ).',
            loc_input_datefmt       = '
.$DB->quote( $restored_locale['input_datefmt'] ).',
            loc_timefmt             = '
.$DB->quote( $restored_locale['timefmt'] ).',
            loc_shorttimefmt        = '
.$DB->quote( empty( $restored_locale['shorttimefmt'] ) ? str_replace( ':s', '', $restored_locale['timefmt'] ) : $restored_locale['shorttimefmt'] ).',
            loc_input_timefmt       = '
.$DB->quote( $restored_locale['input_timefmt'] ).',
            loc_startofweek         = '
.$DB->quote( $restored_locale['startofweek'] ).',
            loc_name                = '
.$DB->quote( $restored_locale['name'] ).',
            loc_messages            = '
.$DB->quote( $restored_locale['messages'] ).',
            loc_priority            = '
.$DB->quote( $restored_locale['priority'] ).',
            loc_transliteration_map = '
.$DB->quote( $transliteration_map ).'
            WHERE loc_locale = '
.$DB->quote( $restored_locale_key ) );
    }
}


/**
 * Check default locale and enable if it is not yet
 * Used after each upgrade process
 */
function locale_check_default()
{
    global
$DB, $Settings, $default_locale, $locales;

   
// Get default locale from DB:
   
$current_default_locale = $Settings->get( 'default_locale' );

    if( isset(
$locales[ $current_default_locale ] ) && $locales[ $current_default_locale ]['enabled'] )
    {    
// Current default locale is correct and enabled
        // Nothing to fix, Exit here:
       
return;
    }

    if( ! isset(
$locales[ $current_default_locale ] ) )
    {    
// If current default locale is wrong and doesn't exist anymore then use default locale from config:
       
$current_default_locale = $default_locale;
       
// Fix wrong default locale to correct value:
       
$Settings->set( 'default_locale', $current_default_locale );
       
$Settings->dbupdate();
    }

   
// Get a row of current default locale in order to check if it is enabled:
   
$SQL = new SQL( 'Check if current default locale is enabled' );
   
$SQL->SELECT( 'loc_enabled' );
   
$SQL->FROM( 'T_locales' );
   
$SQL->WHERE( 'loc_locale = '.$DB->quote( $current_default_locale ) );
   
$current_default_locale_enabled = $DB->get_var( $SQL );

    if(
$current_default_locale_enabled !== NULL )
    {    
// Current default locale exists in DB
       
if( ! $current_default_locale_enabled )
        {    
// Enable current default locale if it is not enabled yet:
           
$DB->query( 'UPDATE T_locales
                  SET loc_enabled = 1
                WHERE loc_locale = '
.$DB->quote( $current_default_locale ) );
        }
    }
    else
    {    
// No record of default locale

        // Make sure default transliteration_map is set
       
$transliteration_map = '';
        if( isset(
$locales[ $current_default_locale ]['transliteration_map'] ) && is_array( $locales[ $current_default_locale ]['transliteration_map'] ) )
        {
           
$transliteration_map = base64_encode( serialize( $locales[ $current_default_locale ]['transliteration_map'] ) );
        }

       
// Insert default locale into DB in order to make it enabled:
       
$DB->query( 'INSERT INTO T_locales '
           
.'( loc_locale, loc_datefmt, loc_longdatefmt, loc_extdatefmt, loc_input_datefmt, loc_timefmt, loc_shorttimefmt, loc_input_timefmt, '
           
.'loc_startofweek, loc_name, loc_messages, loc_priority, '
           
.'loc_transliteration_map, loc_enabled ) '
           
.'VALUES ( '
           
.$DB->quote( $current_default_locale ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['datefmt'] ).', '
           
.$DB->quote( empty( $locales[ $current_default_locale ]['longdatefmt'] ) ? str_replace( 'y', 'Y', $locales[ $current_default_locale ]['datefmt'] ) : $locales[ $current_default_locale ]['longdatefmt'] ).', '
           
.$DB->quote( empty( $locales[ $current_default_locale ]['extdatefmt'] ) ? str_replace( array( '.'. '-', '\\', '/' ), ' ', str_replace( array( 'y', 'm' ), array( 'Y', 'Y' ), $locales[ $current_default_locale ]['datefmt'] ) ) : $locales[ $current_default_locale ]['extdatefmt'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['input_datefmt'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['timefmt'] ).', '
           
.$DB->quote( empty( $locales[ $current_default_locale ]['shorttimefmt'] ) ? str_replace( ':s', '', $locales[ $current_default_locale ]['timefmt'] ) : $locales[ $current_default_locale ]['shorttimefmt'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['input_timefmt'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['startofweek'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['name'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['messages'] ).', '
           
.$DB->quote( $locales[ $current_default_locale ]['priority'] ).', '
           
.$DB->quote( $transliteration_map ).', '
           
.'1 )' );
    }

}


/**
 * Returns the locale's default field value
 *
 * @param string Locale field
 * @param string Locale name, '#' to get default value not specific to a locale
 * @param mixed Value to return if locale field is empty and no default value is assigned
 * @return mixed Locale field value
 */
function locale_get( $field, $locale = NULL, $default = NULL )
{
    global
$locales;

    if(
is_null( $locale ) )
    {
        global
$current_locale;
       
$locale = $current_locale;
    }

    if(
$locale == '#' || empty( $locales[$locale][$field] ) )
    {
       
$default_values = array(
           
'datefmt' => 'Y-m-d',
           
'longdatefmt' => 'Y-m-d',
           
'extdatefmt' => 'M d, Y',
           
'input_datefmt' => 'Y-m-d',
           
'timefmt' => 'H:i:s',
           
'shorttimefmt' => 'H:i',
           
'input_timefmt' => 'H:i:s'
       
);

        switch(
$field )
        {
            case
'longdatefmt':
                if( isset(
$locales[$locale]['datefmt'] ) )
                {
                    return
str_replace( 'y', 'Y', $locales[$locale]['datefmt'] );
                }
                else
                {
                    return isset(
$default ) ? $default : $default_values['longdatefmt'];
                }
                break;

            case
'extdatefmt':
                if( isset(
$locales[$locale]['datefmt'] ) )
                {
                    return
str_replace( array( '.', '-', '\\', '/' ), ' ', str_replace( array( 'm', 'y' ), array( 'M', 'Y' ), $locales[$locale]['datefmt'] ) );
                }
                else
                {
                    return isset(
$default ) ? $default : $default_values['extdatefmt'];
                }
                break;

            case
'shorttimefmt':
                if( isset(
$locales[$locale]['timefmt'] ) )
                {
                    return
str_replace( ':s', '', $locales[$locale]['timefmt'] );
                }
                else
                {
                    return isset(
$default ) ? $default : $default_values['shorttimefmt'];
                }
                break;

            default:
                return isset(
$default_values[$field] ) ? $default_values[$field] : ( isset( $default ) ? $default : NULL );
        }
    }
    else
    {
        return
$locales[$locale][$field];
    }
}


/**
 * Set default values of locales formats
 *
 * @param array Locale
 * @param array Source locale
 */
function locale_set_default_formats( & $locale, $source_locale = NULL )
{
    if(
is_null( $source_locale ) )
    {
       
$source_locale = $locale;
    }

    foreach(
$source_locale as $l_key => $l_value )
    {
        if(
$default_value = locale_get( $l_key, $source_locale ) )
        {
           
$locale[$l_key] = $default_value;
        }

    }
}


/**
 * Provide normalizer_normalize for older versions of PHP < 5.3.0 and PECL intl < 1.0.0
 *
 * Normalizes the input provided and returns the normalized string
 *
 * @param string The input string to normalize
 * @param integer One of the normalization forms
 * @return string The normalized string or FALSE if an error occurred
 */
if( ! function_exists( 'normalizer_normalize' ) )
{
    function
normalizer_normalize( $input, $form = NULL )
    {
       
$transliteration = array(
           
'IJ'=>'I','Ö'=>'O','Œ'=>'O','Ü'=>'U','ä'=>'a','æ'=>'a',
           
'ij'=>'i','ö'=>'o','œ'=>'o','ü'=>'u','ß'=>'s','ſ'=>'s',
           
'À'=>'A','Á'=>'A','Â'=>'A','Ã'=>'A','Ä'=>'A','Å'=>'A',
           
'Æ'=>'A','Ā'=>'A','Ą'=>'A','Ă'=>'A','Ç'=>'C','Ć'=>'C',
           
'Č'=>'C','Ĉ'=>'C','Ċ'=>'C','Ď'=>'D','Đ'=>'D','È'=>'E',
           
'É'=>'E','Ê'=>'E','Ë'=>'E','Ē'=>'E','Ę'=>'E','Ě'=>'E',
           
'Ĕ'=>'E','Ė'=>'E','Ĝ'=>'G','Ğ'=>'G','Ġ'=>'G','Ģ'=>'G',
           
'Ĥ'=>'H','Ħ'=>'H','Ì'=>'I','Í'=>'I','Î'=>'I','Ï'=>'I',
           
'Ī'=>'I','Ĩ'=>'I','Ĭ'=>'I','Į'=>'I','İ'=>'I','Ĵ'=>'J',
           
'Ķ'=>'K','Ľ'=>'K','Ĺ'=>'K','Ļ'=>'K','Ŀ'=>'K','Ł'=>'L',
           
'Ñ'=>'N','Ń'=>'N','Ň'=>'N','Ņ'=>'N','Ŋ'=>'N','Ò'=>'O',
           
'Ó'=>'O','Ô'=>'O','Õ'=>'O','Ø'=>'O','Ō'=>'O','Ő'=>'O',
           
'Ŏ'=>'O','Ŕ'=>'R','Ř'=>'R','Ŗ'=>'R','Ś'=>'S','Ş'=>'S',
           
'Ŝ'=>'S','Ș'=>'S','Š'=>'S','Ť'=>'T','Ţ'=>'T','Ŧ'=>'T',
           
'Ț'=>'T','Ù'=>'U','Ú'=>'U','Û'=>'U','Ū'=>'U','Ů'=>'U',
           
'Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U','Ŵ'=>'W','Ŷ'=>'Y',
           
'Ÿ'=>'Y','Ý'=>'Y','Ź'=>'Z','Ż'=>'Z','Ž'=>'Z','à'=>'a',
           
'á'=>'a','â'=>'a','ã'=>'a','ā'=>'a','ą'=>'a','ă'=>'a',
           
'å'=>'a','ç'=>'c','ć'=>'c','č'=>'c','ĉ'=>'c','ċ'=>'c',
           
'ď'=>'d','đ'=>'d','è'=>'e','é'=>'e','ê'=>'e','ë'=>'e',
           
'ē'=>'e','ę'=>'e','ě'=>'e','ĕ'=>'e','ė'=>'e','ƒ'=>'f',
           
'ĝ'=>'g','ğ'=>'g','ġ'=>'g','ģ'=>'g','ĥ'=>'h','ħ'=>'h',
           
'ì'=>'i','í'=>'i','î'=>'i','ï'=>'i','ī'=>'i','ĩ'=>'i',
           
'ĭ'=>'i','į'=>'i','ı'=>'i','ĵ'=>'j','ķ'=>'k','ĸ'=>'k',
           
'ł'=>'l','ľ'=>'l','ĺ'=>'l','ļ'=>'l','ŀ'=>'l','ñ'=>'n',
           
'ń'=>'n','ň'=>'n','ņ'=>'n','ʼn'=>'n','ŋ'=>'n','ò'=>'o',
           
'ó'=>'o','ô'=>'o','õ'=>'o','ø'=>'o','ō'=>'o','ő'=>'o',
           
'ŏ'=>'o','ŕ'=>'r','ř'=>'r','ŗ'=>'r','ś'=>'s','š'=>'s',
           
'ť'=>'t','ù'=>'u','ú'=>'u','û'=>'u','ū'=>'u','ů'=>'u',
           
'ű'=>'u','ŭ'=>'u','ũ'=>'u','ų'=>'u','ŵ'=>'w','ÿ'=>'y',
           
'ý'=>'y','ŷ'=>'y','ż'=>'z','ź'=>'z','ž'=>'z','Α'=>'A',
           
'Ά'=>'A','Ἀ'=>'A','Ἁ'=>'A','Ἂ'=>'A','Ἃ'=>'A','Ἄ'=>'A',
           
'Ἅ'=>'A','Ἆ'=>'A','Ἇ'=>'A','ᾈ'=>'A','ᾉ'=>'A','ᾊ'=>'A',
           
'ᾋ'=>'A','ᾌ'=>'A','ᾍ'=>'A','ᾎ'=>'A','ᾏ'=>'A','Ᾰ'=>'A',
           
'Ᾱ'=>'A','Ὰ'=>'A','ᾼ'=>'A','Β'=>'B','Γ'=>'G','Δ'=>'D',
           
'Ε'=>'E','Έ'=>'E','Ἐ'=>'E','Ἑ'=>'E','Ἒ'=>'E','Ἓ'=>'E',
           
'Ἔ'=>'E','Ἕ'=>'E','Ὲ'=>'E','Ζ'=>'Z','Η'=>'I','Ή'=>'I',
           
'Ἠ'=>'I','Ἡ'=>'I','Ἢ'=>'I','Ἣ'=>'I','Ἤ'=>'I','Ἥ'=>'I',
           
'Ἦ'=>'I','Ἧ'=>'I','ᾘ'=>'I','ᾙ'=>'I','ᾚ'=>'I','ᾛ'=>'I',
           
'ᾜ'=>'I','ᾝ'=>'I','ᾞ'=>'I','ᾟ'=>'I','Ὴ'=>'I','ῌ'=>'I',
           
'Θ'=>'T','Ι'=>'I','Ί'=>'I','Ϊ'=>'I','Ἰ'=>'I','Ἱ'=>'I',
           
'Ἲ'=>'I','Ἳ'=>'I','Ἴ'=>'I','Ἵ'=>'I','Ἶ'=>'I','Ἷ'=>'I',
           
'Ῐ'=>'I','Ῑ'=>'I','Ὶ'=>'I','Κ'=>'K','Λ'=>'L','Μ'=>'M',
           
'Ν'=>'N','Ξ'=>'K','Ο'=>'O','Ό'=>'O','Ὀ'=>'O','Ὁ'=>'O',
           
'Ὂ'=>'O','Ὃ'=>'O','Ὄ'=>'O','Ὅ'=>'O','Ὸ'=>'O','Π'=>'P',
           
'Ρ'=>'R','Ῥ'=>'R','Σ'=>'S','Τ'=>'T','Υ'=>'Y','Ύ'=>'Y',
           
'Ϋ'=>'Y','Ὑ'=>'Y','Ὓ'=>'Y','Ὕ'=>'Y','Ὗ'=>'Y','Ῠ'=>'Y',
           
'Ῡ'=>'Y','Ὺ'=>'Y','Φ'=>'F','Χ'=>'X','Ψ'=>'P','Ω'=>'O',
           
'Ώ'=>'O','Ὠ'=>'O','Ὡ'=>'O','Ὢ'=>'O','Ὣ'=>'O','Ὤ'=>'O',
           
'Ὥ'=>'O','Ὦ'=>'O','Ὧ'=>'O','ᾨ'=>'O','ᾩ'=>'O','ᾪ'=>'O',
           
'ᾫ'=>'O','ᾬ'=>'O','ᾭ'=>'O','ᾮ'=>'O','ᾯ'=>'O','Ὼ'=>'O',
           
'ῼ'=>'O','α'=>'a','ά'=>'a','ἀ'=>'a','ἁ'=>'a','ἂ'=>'a',
           
'ἃ'=>'a','ἄ'=>'a','ἅ'=>'a','ἆ'=>'a','ἇ'=>'a','ᾀ'=>'a',
           
'ᾁ'=>'a','ᾂ'=>'a','ᾃ'=>'a','ᾄ'=>'a','ᾅ'=>'a','ᾆ'=>'a',
           
'ᾇ'=>'a','ὰ'=>'a','ᾰ'=>'a','ᾱ'=>'a','ᾲ'=>'a','ᾳ'=>'a',
           
'ᾴ'=>'a','ᾶ'=>'a','ᾷ'=>'a','β'=>'b','γ'=>'g','δ'=>'d',
           
'ε'=>'e','έ'=>'e','ἐ'=>'e','ἑ'=>'e','ἒ'=>'e','ἓ'=>'e',
           
'ἔ'=>'e','ἕ'=>'e','ὲ'=>'e','ζ'=>'z','η'=>'i','ή'=>'i',
           
'ἠ'=>'i','ἡ'=>'i','ἢ'=>'i','ἣ'=>'i','ἤ'=>'i','ἥ'=>'i',
           
'ἦ'=>'i','ἧ'=>'i','ᾐ'=>'i','ᾑ'=>'i','ᾒ'=>'i','ᾓ'=>'i',
           
'ᾔ'=>'i','ᾕ'=>'i','ᾖ'=>'i','ᾗ'=>'i','ὴ'=>'i','ῂ'=>'i',
           
'ῃ'=>'i','ῄ'=>'i','ῆ'=>'i','ῇ'=>'i','θ'=>'t','ι'=>'i',
           
'ί'=>'i','ϊ'=>'i','ΐ'=>'i','ἰ'=>'i','ἱ'=>'i','ἲ'=>'i',
           
'ἳ'=>'i','ἴ'=>'i','ἵ'=>'i','ἶ'=>'i','ἷ'=>'i','ὶ'=>'i',
           
'ῐ'=>'i','ῑ'=>'i','ῒ'=>'i','ῖ'=>'i','ῗ'=>'i','κ'=>'k',
           
'λ'=>'l','μ'=>'m','ν'=>'n','ξ'=>'k','ο'=>'o','ό'=>'o',
           
'ὀ'=>'o','ὁ'=>'o','ὂ'=>'o','ὃ'=>'o','ὄ'=>'o','ὅ'=>'o',
           
'ὸ'=>'o','π'=>'p','ρ'=>'r','ῤ'=>'r','ῥ'=>'r','σ'=>'s',
           
'ς'=>'s','τ'=>'t','υ'=>'y','ύ'=>'y','ϋ'=>'y','ΰ'=>'y',
           
'ὐ'=>'y','ὑ'=>'y','ὒ'=>'y','ὓ'=>'y','ὔ'=>'y','ὕ'=>'y',
           
'ὖ'=>'y','ὗ'=>'y','ὺ'=>'y','ῠ'=>'y','ῡ'=>'y','ῢ'=>'y',
           
'ῦ'=>'y','ῧ'=>'y','φ'=>'f','χ'=>'x','ψ'=>'p','ω'=>'o',
           
'ώ'=>'o','ὠ'=>'o','ὡ'=>'o','ὢ'=>'o','ὣ'=>'o','ὤ'=>'o',
           
'ὥ'=>'o','ὦ'=>'o','ὧ'=>'o','ᾠ'=>'o','ᾡ'=>'o','ᾢ'=>'o',
           
'ᾣ'=>'o','ᾤ'=>'o','ᾥ'=>'o','ᾦ'=>'o','ᾧ'=>'o','ὼ'=>'o',
           
'ῲ'=>'o','ῳ'=>'o','ῴ'=>'o','ῶ'=>'o','ῷ'=>'o','А'=>'A',
           
'Б'=>'B','В'=>'V','Г'=>'G','Д'=>'D','Е'=>'E','Ё'=>'E',
           
'Ж'=>'Z','З'=>'Z','И'=>'I','Й'=>'I','К'=>'K','Л'=>'L',
           
'М'=>'M','Н'=>'N','О'=>'O','П'=>'P','Р'=>'R','С'=>'S',
           
'Т'=>'T','У'=>'U','Ф'=>'F','Х'=>'K','Ц'=>'T','Ч'=>'C',
           
'Ш'=>'S','Щ'=>'S','Ы'=>'Y','Э'=>'E','Ю'=>'Y','Я'=>'Y',
           
'а'=>'A','б'=>'B','в'=>'V','г'=>'G','д'=>'D','е'=>'E',
           
'ё'=>'E','ж'=>'Z','з'=>'Z','и'=>'I','й'=>'I','к'=>'K',
           
'л'=>'L','м'=>'M','н'=>'N','о'=>'O','п'=>'P','р'=>'R',
           
'с'=>'S','т'=>'T','у'=>'U','ф'=>'F','х'=>'K','ц'=>'T',
           
'ч'=>'C','ш'=>'S','щ'=>'S','ы'=>'Y','э'=>'E','ю'=>'Y',
           
'я'=>'Y','ð'=>'d','Ð'=>'D','þ'=>'t','Þ'=>'T','ა'=>'a',
           
'ბ'=>'b','გ'=>'g','დ'=>'d','ე'=>'e','ვ'=>'v','ზ'=>'z',
           
'თ'=>'t','ი'=>'i','კ'=>'k','ლ'=>'l','მ'=>'m','ნ'=>'n',
           
'ო'=>'o','პ'=>'p','ჟ'=>'z','რ'=>'r','ს'=>'s','ტ'=>'t',
           
'უ'=>'u','ფ'=>'p','ქ'=>'k','ღ'=>'g','ყ'=>'q','შ'=>'s',
           
'ჩ'=>'c','ც'=>'t','ძ'=>'d','წ'=>'t','ჭ'=>'c','ხ'=>'k',
           
'ჯ'=>'j','ჰ'=>'h','ʼ'=>"'",'̧' =>'', 'ḩ'=>'h','ʼ'=>"'",
           
'‘'=>"'",'’'=>"'",'ừ'=>'u','ế'=>'e','ả'=>'a','ị'=>'i',
           
'ậ'=>'a','ệ'=>'e','ỉ'=>'i','ộ'=>'o','ồ'=>'o','ề'=>'e',
           
'ơ'=>'o','ạ'=>'a','ẵ'=>'a','ư'=>'u','ắ'=>'a','ằ'=>'a',
           
'ầ'=>'a','ḑ'=>'d','Ḩ'=>'H','Ḑ'=>'D','ḑ'=>'d','ş'=>'s',
           
'ā'=>'a','ţ'=>'t'
       
);

        return
strtr( $input, $transliteration );
    }
}


/**
 * Get currency data from locale
 *
 * @param string Locale code, '#' - to use current currency
 * @param string What return: 'code', 'symbol', 'ID'
 * @return string|boolean Currency data depending on param $return OR FALSE on failed
 */
function locale_currency( $locale = '#', $return = 'code' )
{
    global
$current_locale;

    if(
$locale == '#' )
    {    
// Use current locale:
       
$locale = $current_locale;
    }

   
// Extract country code from locale code:
   
$country_code = strtolower( substr( $locale, 3, 2 ) );

   
$CountryCache = & get_CountryCache();
    if( ! (
$locale_Country = & $CountryCache->get_by_name( $country_code, false, false ) ) )
    {    
// Couldn't detect country by code:
       
return false;
    }

    if( ! (
$country_Currency = & $locale_Country->get_Currency() ) )
    {    
// Couldn't detect currency for the locale country:
       
return false;
    }

   
// Return currency code depending on param $return:
   
switch( $return )
    {
        case
'symbol':
            return
$country_Currency->get( 'shortcut' );

        case
'ID':
            return
$country_Currency->ID;

        case
'code':
        default:
            return
$country_Currency->get( 'code' );
    }
}
?>