-<?php
-/**
- * i18n class for Nucleus CMS
- * written by Takashi Sakamoto as of Feb 03, 2012
- *
- * This includes wrapper functions of iconv and mbstring
- * and mail function with 7bit characters encoder
- * for multibyte processing and includes members related to locale.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * (see nucleus/documentation/index.html#license for more info)
- */
-class i18n
-{
- static private $mode = FALSE;
-
- static private $charset = '';
- static private $language = '';
- static private $script = '';
- static private $region = '';
- static private $locale_list = array();
- static private $timezone = 'UTC';
-
- /**
- * i18n::init
- * Initializing i18n class
- *
- * @static
- * @access public
- * @param string $charset character set
- * @return boolean
- */
- static public function init($charset, $dir)
- {
- /* i18n is already initialized */
- if ( self::$mode )
- {
- return TRUE;
- }
-
- /* make locale list in this Nucleus CMS */
- if ( ($handle = opendir($dir)) === FALSE )
- {
- return FALSE;
- }
- while ($filename = readdir($handle))
- {
- if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
- {
- if ( !in_array($matches[1], self::$locale_list) )
- {
- self::$locale_list[] = $matches[1];
- }
- }
- }
- closedir($handle);
-
- /* set i18n backend and validate character set */
- if ( extension_loaded('iconv') )
- {
- /* this is just for checking the charset. */
- if ( iconv_set_encoding('internal_encoding', $charset)
- && iconv_set_encoding('output_encoding', $charset)
- && iconv_set_encoding('internal_encoding', $charset) )
- {
- self::$charset = $charset;
- self::$mode = 'iconv';
- }
- }
- else if ( extension_loaded('mbstring') )
- {
- /* this is just for checking the charset. */
- if ( mb_http_output($charset)
- && mb_internal_encoding($charset)
- && mb_regex_encoding($charset) )
- {
- self::$charset = $charset;
- self::$mode = 'mbstring';
- }
- }
-
- return TRUE;
- }
-
- /**
- * i18n::get_available_locale_list
- * return available locale list with current charset
- *
- * @static
- * @access public
- * @param void
- * @return array available locale list
- */
- static public function get_available_locale_list()
- {
- return self::$locale_list;
- }
-
- /**
- * i18n::get_current_charset
- * return current charset
- *
- * @static
- * @access public
- * @param void
- * @return string $charset current character set
- */
- static public function get_current_charset()
- {
- return self::$charset;
- }
-
- /**
- * i18n::set_locale
- * Set current locale
- *
- * NOTE:
- * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
- * @link http://www.ietf.org/rfc/rfc5646.txt
- * @see 2. The Language Tag
- *
- * @static
- * @access public
- * @param string $locale
- * @return bool TRUE/FALSE
- *
- */
- static public function set_current_locale($locale)
- {
- if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
- {
- self::$language = $match[1];
- self::$script = $match[2];
- self::$region = $match[3];
- return TRUE;
- }
- return FALSE;
- }
-
- /**
- * i18n::get_locale
- * Get current locale
- *
- * @static
- * @access public
- * @param void
- * @return $locale
- */
- static public function get_current_locale()
- {
- $elements = array(self::$language, self::$script, self::$region);
- return implode('_', $elements);
- }
-
- /**
- * i18n::confirm_default_date_timezone
- * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
- *
- * NOTE:
- * Some private servers are lack of its timezone setting
- * http://www.php.net/manual/en/function.date-default-timezone-set.php
- *
- * @static
- * @access public
- * @param void
- * @return void
- */
- static public function confirm_default_date_timezone()
- {
- if ( function_exists('date_default_timezone_get')
- && FALSE !== ($timezone = @date_default_timezone_get()))
- {
- self::$timezone = $timezone;
- }
- if (function_exists('date_default_timezone_set')) {
- @date_default_timezone_set(self::$timezone);
- }
- return;
- }
-
- /**
- * i18n::get_current_date_timezone()
- * get current timezone
- *
- * @static
- * @access public
- * @param void
- * @return $timezone
- */
- static public function get_date_timezone()
- {
- return self::$timezone;
- }
-
- /**
- * i18n::hen
- * htmlentities wrapper
- *
- * @static
- * @access public
- * @param string $string target string
- * @param string $quotation quotation mode. please refer to the argument of PHP built-in htmlentities
- * @return string escaped string
- */
- static public function hen($string, $quotation=ENT_QUOTES)
- {
- $string = html_entity_decode($string, $quotation, self::$charset);
- return (string) htmlentities($string, $quotation, self::$charset);
- }
-
- /**
- * i18n::hsc
- * htmlspecialchars wrapper
- *
- * NOTE: htmlspecialchars_decode() is ASCII-to-ACII conversion
- * and its target string consists of several letters.
- * There are no problems.
- *
- * @static
- * @access public
- * @param string $string target string
- * @param string $quotation quotation mode. please refer to the argument of PHP built-in htmlspecialchars
- * @return string escaped string
- *
- */
-
- static public function hsc($string, $quotation=ENT_QUOTES)
- {
- $string = htmlspecialchars_decode($string, $quotation);
- return (string) htmlspecialchars($string, $quotation, self::$charset);
- }
-
- /**
- * i18n::convert
- * character set converter
- *
- * @static
- * @access public
- * @param string $string target string binary
- * @param string $from original character set encoding
- * @param string $to target character set encoding
- * @return string converted string
- */
- static public function convert($string, $from, $to='')
- {
- if ( $to == '' )
- {
- $to = self::$charset;
- }
-
- if ( self::$mode == 'iconv' )
- {
- $string = iconv($from, $to.'//TRANSLIT', $string);
- }
- else if ( self::$mode == 'mbstring' )
- {
- $string = mb_convert_encoding($string, $to, $from);
- }
- return (string) $string;
- }
-
- /**
- * i18n::strlen
- * strlen wrapper
- *
- * @static
- * @access public
- * @param string $string target string
- * @return integer the number of letters
- */
- static public function strlen($string)
- {
- $length = 0;
- if ( self::$mode == 'iconv' )
- {
- $length = iconv_strlen($string, self::$charset);
- }
- else if ( self::$mode == 'mbstring' )
- {
- $length = mb_strlen($string, self::$charset);
- }
- else
- {
- $length = strlen($string);
- }
- return (integer) $length;
- }
-
- /**
- * i18n::strpos
- * strpos wrapper
- *
- * @static
- * @access public
- * @param string $haystack string to search
- * @param string $needle string for search
- * @param string $offset the position from which the search should be performed.
- * @return integer/FALSE the numeric position of the first occurrence of needle in haystack
- */
- static public function strpos($haystack, $needle, $offset=0)
- {
- $position = 0;
- if ( self::$mode == 'iconv' )
- {
- $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
- }
- else if ( self::$mode == 'mbstring' )
- {
- $position = mb_strpos($haystack, $needle, $offset, self::$charset);
- }
- else
- {
- $position = strpos($haystack, $needle, $offset);
- }
-
- if ( $position !== FALSE)
- {
- $position = (integer) $position;
- }
- return $position;
- }
-
- /**
- * i18n::strrpos
- * strrpos wrapper
- *
- * @static
- * @access public
- * @param string $haystack string to search
- * @param string $needle string for search
- * @return integer/FALSE the numeric position of the last occurrence of needle in haystack
- */
- static public function strrpos ($haystack, $needle)
- {
- $position = 0;
- if ( self::$mode == 'iconv' )
- {
- $position = iconv_strrpos($haystack, $needle, self::$charset);
- }
- else if ( self::$mode == 'mbstring' )
- {
- $position = mb_strrpos($haystack, $needle, 0, self::$charset);
- }
- else
- {
- $position = strrpos($haystack, $needle, 0);
- }
-
- if ( $position !== FALSE)
- {
- $position = (integer) $position;
- }
- return $position;
- }
-
- /**
- * i18n::substr
- * substr wrapper
- *
- * @static
- * @access public
- * @param string $string string to be cut
- * @param string $start the position of starting
- * @param integer $length the length to be cut
- * @return string the extracted part of string
- */
- static public function substr($string, $start, $length=0)
- {
- $return = '';
- if ( self::$mode == 'iconv' )
- {
- $return = iconv_substr($string, $start, $length, self::$charset);
- }
- else if ( self::$mode == 'mbstring' )
- {
- $return = mb_substr($string, $start, $length, self::$charset);
- }
- else
- {
- $return = strrpos($string, $start, $length);
- }
- return (string) $return;
- }
-
- /**
- * i18n::explode()
- * explode function based on multibyte processing with non-pcre regular expressions
- *
- * NOTE: we SHOULD use preg_split function instead of this,
- * and I hope this is obsoleted near future...
- *
- * preg_split()
- * http://www.php.net/manual/en/function.preg-split.php
- *
- * @static
- * @access public
- * @param string $delimiter singlebyte or multibyte delimiter
- * @param string $target target string
- * @param integer $limit the number of index for returned array
- * @return array array splitted by $delimiter
- */
- static public function explode($delimiter, $target, $limit=0)
- {
- $array = array();
- $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
- if ( preg_match($preg_delimiter, $target) === 0 )
- {
- return (array) $target;
- }
- for ( $count=0; $limit == 0 || $count < $limit; $count++ )
- {
- $offset = self::strpos($target, $delimiter);
- if ( $array != array() && $offset == 0 )
- {
- $array[] = $target;
- break;
- }
- $array[] = self::substr($target, 0, $offset);
- $length = self::strlen($target) - $offset;
- $target = self::substr($target, $offset+1, $length);
- continue;
- }
- return (array) $array;
- }
-
- /**
- * i18n::strftime
- * strftime function based on multibyte processing
- *
- * @static
- * @access public
- * @param string $format format with singlebyte or multibyte
- * @param timestamp $timestamp UNIX timestamp
- * @return string formatted timestamp
- */
- static public function strftime($format, $timestamp='')
- {
- $formatted = '';
-
- if ( $timestamp == '' )
- {
- $timestamp = time();
- }
-
- if ( $format == '%%' )
- {
- return '%';
- }
- else if ( preg_match('#%[^%]#', $format) === 0 )
- {
- return $format;
- }
-
- $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
- $elements = preg_split('#,#', $format);
-
- foreach ( $elements as $element )
- {
- if ( preg_match('#(%[^%])#', $element) )
- {
- $formatted .= strftime($element, $timestamp);
- }
- else if ( $element == '%%' )
- {
- $formatted .= '%';
- }
- else
- {
- $formatted .= $element;
- }
- }
-
- return (string) $formatted;
- }
-
- /**
- * i18n::mail
- * Send mails with headers including 7bit-encoded multibyte string
- *
- * @static
- * @access public
- * @param string $to receivers including singlebyte and multibyte strings, based on RFC 5322
- * @param string $subject subject including singlebyte and multibyte strings
- * @param string $message message including singlebyte and multibyte strings
- * @param string $from senders including singlebyte and multibyte strings, based on RFC 5322
- * @param string(B/Q) $scheme 7bit-encoder scheme based on RFC 2047
- * @return boolean accepted delivery or not
- */
- static public function mail($to, $subject, $message, $from, $scheme='B')
- {
-
- $to = self::mailbox_list_encoder($to, $scheme);
- $subject = self::seven_bit_characters_encoder($subject, $scheme);
- $from = 'From: ' . self::mailbox_list_encoder($from, $scheme);
-
- /*
- * All of 7bit character encoding derives from ISO/IEC 646
- * So we can decide the body's encoding bit count by this regular expression.
- *
- */
- $bitcount = '8bit';
- if ( preg_match('#\A[\x00-\x7f]*\z#', $message) )
- {
- $bitcount = '7bit';
- }
-
- $headers = 'Content-Type: text/html; charset=' . self::$charset . "; format=flowed; delsp=yes\n"
- . "Content-Transfer-Encoding: {$bitcount}\n"
- . "X-Mailer: Nucleus CMS i18n class\n";
-
- return mail($to, $subject, $message, "{$from}\n{$headers}");
- }
-
- /**
- * i18n::mailbox_list_encoder
- * Encode multi byte strings included in mailbox.
- * The format of mailbox is based on RFC 5322, which obsoletes RFC 2822
- *
- * @link http://www.ietf.org/rfc/rfc5322.txt
- * @see 3.4. Address Specification
- *
- * @static
- * @access private
- * @param string $mailbox_list mailbox list
- * @return string encoded string
- *
- */
- static private function mailbox_list_encoder ($mailbox_list, $scheme='B')
- {
- $encoded_mailboxes = array();
- $mailboxes = preg_split('#,#', $mailbox_list);
- foreach ( $mailboxes as $mailbox )
- {
- if ( preg_match("#^([^,]+)?<([^,]+)?@([^,]+)?>$#", $mailbox, $match) )
- {
- $display_name = self::seven_bit_characters_encoder(trim($match[1]), $scheme);
- $local_part = trim($match[2]);
- $domain = trim($match[3]);
- $encoded_mailboxes[] = "{$name} <{$local_part}@{$domain}>";
- }
- else if ( preg_match("#([^,]+)?@([^,]+)?#", $mailbox) )
- {
- $encoded_mailboxes[] = $mailbox;
- }
- else
- {
- continue;
- }
- }
- if ( $encoded_mailboxes == array() )
- {
- return FALSE;
- }
- return implode(',', $encoded_mailboxes);
- }
-
- /**
- * i18n::seven_bit_characters_encoder
- * Encoder into 7bit ASCII expression for Non-ASCII Text based on RFC 2047.
- *
- * @link http://www.ietf.org/rfc/rfc2047.txt
- * @see 2. Syntax of encoded-words
- *
- * NOTE: RFC 2047 has a ambiguousity for dealing with 'linear-white-space'.
- * This causes a trouble related to line breaking between single byte and multi-byte strings.
- * To avoid this, single byte string is encoded as well as multi byte string here.
- *
- * NOTE: RFC 2231 also defines the way to use non-ASCII characters in MIME header.
- * http://www.ietf.org/rfc/rfc2231.txt
- *
- * NOTE: iconv extension give the same functions as this in PHP5
- * iconv_mime_encode():
- * http://www.php.net/manual/en/function.iconv-mime-encode.php
- *
- * @static
- * @access private
- * @param string $charset Character set encoding
- * @param string $type type of 7 bit encoding, should be 'B' or 'Q'
- * @param string $string Target string with header field
- * @return string encoded string
- *
- */
- static private function seven_bit_characters_encoder($string, $scheme='B')
- {
- if ( $scheme != 'Q' )
- {
- $scheme = 'B';
- }
- $header = chr(13) . chr(10) . chr(32) . '=?' . self::$charset . "?{$scheme}?";
- $footer = "?=";
- $restriction = 78 - strlen($header) - strlen($footer) ;
-
- $encoded_words = array();
- for ( $i = 0; $i < self::strlen($string); $i++ )
- {
- if ( $scheme == 'B' )
- {
- if ( $i == 0 )
- {
- $letters = '';
- }
-
- $letter = self::substr($string, $i, 1);
- $expected_length = strlen($letters) + strlen($letter) * 4 / 3;
-
- if ( $expected_length > $restriction )
- {
- $encoded_text = self::b_encoder($letters);
- $encoded_words[] = "{$header}{$encoded_text}{$footer}";
- $letters = '';
- }
-
- $letters .= $letter;
-
- if ( $i == self::strlen($string) - 1 )
- {
- $encoded_text = self::b_encoder($letters);
- $encoded_words[] = "{$header}{$encoded_text}{$footer}";
- break;
- }
- continue;
- }
- else
- {
- if ( $i == 0 )
- {
- $encoded_text = '';
- }
-
- $encoded_letter = self::q_encoder(self::substr($string, $i, 1));
- $expected_length = strlen($encoded_text) + strlen($encoded_letter);
-
- if ( $expected_length > $restriction )
- {
- $encoded_words[] = "{$header}{$encoded_text}{$footer}";
- $letters = '';
- }
-
- $encoded_text .= $encoded_letter;
-
- if ( $i == self::strlen($string) - 1 )
- {
- $encoded_words[] = "{$header}{$encoded_text}{$footer}";
- break;
- }
- continue;
- }
- }
-
- return implode('', $encoded_words);
- }
-
- /**
- * B encoder according to RFC 2047.
- * The "B" encoding is identical to the "BASE64" encoding defined by RFC 4648.
- *
- * @link http://www.ietf.org/rfc/rfc4648.txt
- * @see 6.8. Base64 Content-Transfer-Encoding
- *
- * NOTE: According to RFC 4648
- * (1) The final quantum of encoding input is an integral multiple of 24 bits;
- * here, the final unit of encoded output will be an integral multiple
- * of 4 characters with no "=" padding.
- * (2) The final quantum of encoding input is exactly 8 bits; here,
- * the final unit of encoded output will be two characters followed
- * by two "=" padding characters.
- * (3) The final quantum of encoding input is exactly 16 bits; here,
- * the final unit of encoded output will be three characters followed
- * by one "=" padding character.
- *
- * @static
- * @access private
- * @param string $target targetted string
- * @return string encoded string
- */
- static private function b_encoder($target)
- {
- return base64_encode($target);
- }
-
- /**
- * Q encoder according to RFC 2047.
- * The "Q" encoding is similar to "Quoted-Printable" content-transfer-encoding defined in RFC 2045,
- * but the "Q" encoding and the "Quoted-Printable" are different a bit.
- *
- * @link http://www.ietf.org/rfc/rfc2047.txt
- * @see 4.2. The "Q" encoding
- *
- * NOTE: According to RFC 2047
- * (1) Any 8-bit value may be represented by a "=" followed by two hexadecimal digits.
- * For example, if the character set in use were ISO-8859-1,
- * the "=" character would thus be encoded as "=3D", and a SPACE by "=20".
- * (Upper case should be used for hexadecimal digits "A" through "F".)
- * (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
- * represented as "_" (underscore, ASCII 95.).
- * (This character may not pass through some internetwork mail gateways,
- * but its use will greatly enhance readability of "Q" encoded data
- * with mail readers that do not support this encoding.)
- * Note that the "_" always represents hexadecimal 20,
- * even if the SPACE character occupies a different code position
- * in the character set in use.
- * (3) 8-bit values which correspond to printable ASCII characters
- * other than "=", "?", and "_" (underscore), MAY be represented as those characters.
- * (But see section 5 for restrictions.)
- * In particular, SPACE and TAB MUST NOT be represented as themselves within encoded words.
- *
- * @static
- * @access private
- * @param string $target targetted string
- * @return string encoded string
- */
- static private function q_encoder($target)
- {
- $string = '';
-
- for ( $i = 0; $i < strlen($target); $i++ )
- {
- $letter = substr ($target, $i, 1);
- $order = ord($letter);
-
- // Printable ASCII characters without "=", "?", "_"
- if ((33 <= $order && $order <= 60)
- || (62 == $order)
- || (64 <= $order && $order <= 94)
- || (96 <= $order && $order <= 126))
- {
- $string .= strtoupper(dechex($order));
- }
- // Space shuold be encoded as the same strings as "_"
- else if ($order == 32)
- {
- $string .= '_';
- }
- // Other characters
- else
- {
- $string .= '=' . strtoupper(dechex($order));
- }
- }
-
- return $string;
- }
-
- /**
- * i18n::convert_locale_to_old_language_file_name()
- * NOTE: this should be obsoleted near future.
- *
- * @static
- * @access public
- * @param string $target_locale locale name as language_script_region
- * @return string old language file name
- */
- static public function convert_locale_to_old_language_file_name($target_locale)
- {
- $target_language = '';
- foreach ( self::$lang_refs as $language => $locale )
- {
- if ( preg_match('#-#', $language) )
- {
- if ( $target_locale . '.' . self::$charset == $locale )
- {
- $target_language = $language;
- break;
- }
- }
- else if ( $target_locale == $locale )
- {
- $target_language = $language;
- }
- }
- return $target_language;
- }
-
- /**
- * i18n::convert_old_language_file_name_to_locale()
- * NOTE: this should be obsoleted near future.
- *
- * @static
- * @access public
- * @param string $target_language old language file name
- * @return string locale name as language_script_region
- */
- static public function convert_old_language_file_name_to_locale($target_language)
- {
- $target_locale = '';
- foreach ( self::$lang_refs as $language => $locale )
- {
- if ( $target_language == $language )
- {
- if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
- {
- $target_locale = $match[1];
- }
- else
- {
- $target_locale = $locale;
- }
- break;
- }
- }
- return $target_locale;
- }
-
- /**
- * i18n::$lang_refs
- * reference table to convert old and new way to name language files.
- * NOTE: this should be obsoleted as soon as possible.
- *
- * @static
- * @access private
- */
- static private $lang_refs = array(
- "english" => "en_Latn_US",
- "english-utf8" => "en_Latn_US.UTF-8",
- "bulgarian" => "bg_Cyrl_BG",
- "finnish" => "fi_Latn_FU",
- "catalan" => "ca_Latn_ES",
- "french" => "fr_Latn_FR",
- "russian" => "ru_Cyrl_RU",
- "chinese" => "zh_Hans_CN",
- "simchinese" => "zh_Hans_CN",
- "chineseb5" => "zh_Hant_TW",
- "traditional_chinese" => "zh_Hant_TW",
- "galego" => "gl_Latn_ES",
- "german" => "de_Latn_DE",
- "korean-utf" => "ko_Kore_KR.UTF-8",
- "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
- "slovak" => "sk_Latn_SK",
- "czech" => "cs_Latn_CZ",
- "hungarian" => "hu_Latn_HU",
- "latvian" => "lv_Latn_LV",
- "nederlands" => "nl_Latn_NL",
- "italiano" => "it_Latn_IT",
- "persian" => "fa_Arab_IR",
- "spanish" => "es_Latn_ES",
- "spanish-utf8" => "es_Latn_ES.UTF-8",
- "japanese-euc" => "ja_Jpan_JP.EUC-JP",
- "japanese-utf8" => "ja_Jpan_JP.UTF-8",
- "portuguese_brazil" => "pt_Latn_BR"
- );
-}
+<?php\r
+/**\r
+ * i18n class for Nucleus CMS\r
+ * written by Takashi Sakamoto as of Feb 03, 2012\r
+ * \r
+ * This includes wrapper functions of iconv and mbstring\r
+ * for multibyte processing and includes parameters related to locale.\r
+ * \r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * (see nucleus/documentation/index.html#license for more info)\r
+ *\r
+ * @license http://nucleuscms.org/license.txt GNU General Public License\r
+ * @copyright Copyright (C) 2002-2011 The Nucleus Group\r
+ * @version $Id: i18n.php 1678 2012-02-26 07:31:36Z sakamocchi $\r
+ */\r
+class i18n\r
+{\r
+ static private $mode = FALSE;\r
+ \r
+ static private $charset = '';\r
+ static private $language = '';\r
+ static private $script = '';\r
+ static private $region = '';\r
+ static private $locale_list = array();\r
+ static private $timezone = 'UTC';\r
+ \r
+ /**\r
+ * i18n::init\r
+ * Initializing i18n class\r
+ * \r
+ * @static\r
+ * @param string $charset character set\r
+ * @return boolean \r
+ */\r
+ static public function init($charset, $dir)\r
+ {\r
+ /* i18n is already initialized */\r
+ if ( self::$mode )\r
+ {\r
+ return TRUE;\r
+ }\r
+ \r
+ /* make locale list in this Nucleus CMS */\r
+ if ( ($handle = opendir($dir)) === FALSE )\r
+ {\r
+ return FALSE;\r
+ }\r
+ while ($filename = readdir($handle))\r
+ {\r
+ if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )\r
+ {\r
+ if ( !in_array($matches[1], self::$locale_list) )\r
+ {\r
+ self::$locale_list[] = $matches[1];\r
+ }\r
+ }\r
+ }\r
+ closedir($handle);\r
+ \r
+ /* set i18n backend and validate character set */\r
+ if ( extension_loaded('iconv') )\r
+ {\r
+ /* this is just for checking the charset. */\r
+ if ( iconv_set_encoding('internal_encoding', $charset)\r
+ && iconv_set_encoding('output_encoding', $charset)\r
+ && iconv_set_encoding('internal_encoding', $charset) )\r
+ {\r
+ self::$charset = $charset;\r
+ self::$mode = 'iconv';\r
+ }\r
+ }\r
+ else if ( extension_loaded('mbstring') )\r
+ {\r
+ /* this is just for checking the charset. */\r
+ if ( mb_http_output($charset)\r
+ && mb_internal_encoding($charset)\r
+ && mb_regex_encoding($charset) )\r
+ {\r
+ self::$charset = $charset;\r
+ self::$mode = 'mbstring';\r
+ }\r
+ }\r
+ \r
+ return TRUE;\r
+ }\r
+ \r
+ /**\r
+ * i18n::get_available_locale_list\r
+ * return available locale list with current charset\r
+ * \r
+ * @static\r
+ * @param void\r
+ * @return array available locale list\r
+ */\r
+ static public function get_available_locale_list()\r
+ {\r
+ return self::$locale_list;\r
+ }\r
+ \r
+ /**\r
+ * i18n::get_current_charset\r
+ * return current charset\r
+ * \r
+ * @static\r
+ * @param void\r
+ * @return string $charset current character set\r
+ */\r
+ static public function get_current_charset()\r
+ {\r
+ return self::$charset;\r
+ }\r
+ \r
+ /**\r
+ * i18n::set_locale\r
+ * Set current locale\r
+ * \r
+ * NOTE:\r
+ * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.\r
+ * @link http://www.ietf.org/rfc/rfc5646.txt\r
+ * @see 2. The Language Tag\r
+ * \r
+ * @static\r
+ * @param string $locale\r
+ * @return bool TRUE/FALSE\r
+ * \r
+ */\r
+ static public function set_current_locale($locale)\r
+ {\r
+ if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )\r
+ {\r
+ self::$language = $match[1];\r
+ self::$script = $match[2];\r
+ self::$region = $match[3];\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+ }\r
+ \r
+ /**\r
+ * i18n::get_locale\r
+ * Get current locale\r
+ * \r
+ * @static\r
+ * @param void\r
+ * @return $locale\r
+ */\r
+ static public function get_current_locale()\r
+ {\r
+ $elements = array(self::$language, self::$script, self::$region);\r
+ return implode('_', $elements);\r
+ }\r
+ \r
+ /**\r
+ * i18n::confirm_default_date_timezone\r
+ * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.\r
+ * \r
+ * NOTE:\r
+ * Some private servers are lack of its timezone setting\r
+ * http://www.php.net/manual/en/function.date-default-timezone-set.php\r
+ * \r
+ * @static\r
+ * @param void\r
+ * @return void\r
+ */\r
+ static public function confirm_default_date_timezone()\r
+ {\r
+ if ( function_exists('date_default_timezone_get') \r
+ && FALSE !== ($timezone = @date_default_timezone_get()))\r
+ {\r
+ self::$timezone = $timezone;\r
+ }\r
+ if (function_exists('date_default_timezone_set')) {\r
+ @date_default_timezone_set(self::$timezone);\r
+ }\r
+ return;\r
+ }\r
+ \r
+ /**\r
+ * i18n::get_current_date_timezone()\r
+ * get current timezone\r
+ * \r
+ * @static\r
+ * @param void\r
+ * @return $timezone\r
+ */\r
+ static public function get_date_timezone()\r
+ {\r
+ return self::$timezone;\r
+ }\r
+ \r
+ /**\r
+ * i18n::convert\r
+ * character set converter\r
+ * \r
+ * @static\r
+ * @param string $string target string binary\r
+ * @param string $from original character set encoding\r
+ * @param string $to target character set encoding\r
+ * @return string converted string\r
+ */\r
+ static public function convert($string, $from, $to='')\r
+ {\r
+ if ( $to == '' )\r
+ {\r
+ $to = self::$charset;\r
+ }\r
+ \r
+ if ( self::$mode == 'iconv' )\r
+ {\r
+ $string = iconv($from, $to.'//TRANSLIT', $string);\r
+ }\r
+ else if ( self::$mode == 'mbstring' )\r
+ {\r
+ $string = mb_convert_encoding($string, $to, $from);\r
+ }\r
+ return (string) $string;\r
+ }\r
+ \r
+ /**\r
+ * i18n::strlen\r
+ * strlen wrapper\r
+ * \r
+ * @static\r
+ * @param string $string target string\r
+ * @return integer the number of letters\r
+ */\r
+ static public function strlen($string)\r
+ {\r
+ $length = 0;\r
+ if ( self::$mode == 'iconv' )\r
+ {\r
+ $length = iconv_strlen($string, self::$charset);\r
+ }\r
+ else if ( self::$mode == 'mbstring' )\r
+ {\r
+ $length = mb_strlen($string, self::$charset);\r
+ }\r
+ else\r
+ {\r
+ $length = strlen($string);\r
+ }\r
+ return (integer) $length;\r
+ }\r
+ \r
+ /**\r
+ * i18n::strpos\r
+ * strpos wrapper\r
+ * \r
+ * @static\r
+ * @param string $haystack string to search\r
+ * @param string $needle string for search\r
+ * @param integer $offset the position from which the search should be performed. \r
+ * @return integer/FALSE the numeric position of the first occurrence of needle in haystack\r
+ */\r
+ static public function strpos($haystack, $needle, $offset=0)\r
+ {\r
+ $position = 0;\r
+ if ( self::$mode == 'iconv' )\r
+ {\r
+ $position = iconv_strpos($haystack, $needle, $offset, self::$charset);\r
+ }\r
+ else if ( self::$mode == 'mbstring' )\r
+ {\r
+ $position = mb_strpos($haystack, $needle, $offset, self::$charset);\r
+ }\r
+ else\r
+ {\r
+ $position = strpos($haystack, $needle, $offset);\r
+ }\r
+ \r
+ if ( $position !== FALSE)\r
+ {\r
+ $position = (integer) $position;\r
+ }\r
+ return $position;\r
+ }\r
+ \r
+ /**\r
+ * i18n::strrpos\r
+ * strrpos wrapper\r
+ * \r
+ * @static\r
+ * @param string $haystack string to search\r
+ * @param string $needle string for search\r
+ * @return integer/FALSE the numeric position of the last occurrence of needle in haystack\r
+ */\r
+ static public function strrpos ($haystack, $needle)\r
+ {\r
+ $position = 0;\r
+ if ( self::$mode == 'iconv' )\r
+ {\r
+ $position = iconv_strrpos($haystack, $needle, self::$charset);\r
+ }\r
+ else if ( self::$mode == 'mbstring' )\r
+ {\r
+ $position = mb_strrpos($haystack, $needle, 0, self::$charset);\r
+ }\r
+ else\r
+ {\r
+ $position = strrpos($haystack, $needle, 0);\r
+ }\r
+ \r
+ if ( $position !== FALSE)\r
+ {\r
+ $position = (integer) $position;\r
+ }\r
+ return $position;\r
+ }\r
+ \r
+ /**\r
+ * i18n::substr\r
+ * substr wrapper\r
+ * \r
+ * @static\r
+ * @param string $string string to be cut\r
+ * @param string $start the position of starting\r
+ * @param integer $length the length to be cut\r
+ * @return string the extracted part of string\r
+ */\r
+ static public function substr($string, $start, $length=0)\r
+ {\r
+ $return = '';\r
+ if ( self::$mode == 'iconv' )\r
+ {\r
+ $return = iconv_substr($string, $start, $length, self::$charset);\r
+ }\r
+ else if ( self::$mode == 'mbstring' )\r
+ {\r
+ $return = mb_substr($string, $start, $length, self::$charset);\r
+ }\r
+ else\r
+ {\r
+ $return = strrpos($string, $start, $length);\r
+ }\r
+ return (string) $return;\r
+ }\r
+ \r
+ /**\r
+ * i18n::strftime\r
+ * strftime function based on multibyte processing\r
+ * \r
+ * @static\r
+ * @param string $format format with singlebyte or multibyte\r
+ * @param timestamp $timestamp UNIX timestamp\r
+ * @return string formatted timestamp\r
+ */\r
+ static public function strftime($format, $timestamp='')\r
+ {\r
+ $formatted = '';\r
+ \r
+ if ( $timestamp == '' )\r
+ {\r
+ $timestamp = time();\r
+ }\r
+ \r
+ if ( $format == '%%' )\r
+ {\r
+ return '%';\r
+ }\r
+ else if ( preg_match('#%[^%]#', $format) === 0 )\r
+ {\r
+ return $format;\r
+ }\r
+ \r
+ $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');\r
+ $elements = preg_split('#,#', $format);\r
+ \r
+ foreach ( $elements as $element )\r
+ {\r
+ if ( preg_match('#(%[^%])#', $element) )\r
+ {\r
+ $formatted .= strftime($element, $timestamp);\r
+ }\r
+ else if ( $element == '%%' )\r
+ {\r
+ $formatted .= '%';\r
+ }\r
+ else\r
+ {\r
+ $formatted .= $element;\r
+ }\r
+ }\r
+ \r
+ return (string) $formatted;\r
+ }\r
+ \r
+ /**\r
+ * i18n::formatted_datetime()\r
+ * return formatted datetime string\r
+ * \r
+ * Date and Time Formats\r
+ * @link http://www.w3.org/TR/NOTE-datetime\r
+ * \r
+ * Working with Time Zones\r
+ * @link http://www.w3.org/TR/timezone/\r
+ * \r
+ * @param String $format time expression format\r
+ * @param String $timestamp UNIX timestamp\r
+ * @param Integer $offset timestamp offset\r
+ * @return String formatted datetime\r
+ */\r
+ static public function formatted_datetime($format, $timestamp, $offset=0)\r
+ {\r
+ $suffix = '';\r
+ $string = '';\r
+ \r
+ switch ( $format )\r
+ {\r
+ case 'mysql':\r
+ /*\r
+ * MySQL 5.0 Reference Manual\r
+ * 10.3.1. The DATE, DATETIME, and TIMESTAMP Types\r
+ * http://dev.mysql.com/doc/refman/5.0/en/datetime.html\r
+ */\r
+ $timestamp += $offset;\r
+ $format = '%Y-%m-%d %H:%M:%S';\r
+ $suffix ='';\r
+ break;\r
+ case 'rfc822':\r
+ /*\r
+ * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
+ * 5. DATE AND TIME SPECIFICATION\r
+ * http://www.ietf.org/rfc/rfc0822.txt\r
+ */\r
+ $format = '%a, %d %m %y %H:%M:%S ';\r
+ if ( $offset < 0 )\r
+ {\r
+ $suffix = '-';\r
+ $offset = -$offset;\r
+ }\r
+ else\r
+ {\r
+ $suffix = '+';\r
+ }\r
+ \r
+ $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
+ break;\r
+ case 'rfc822GMT':\r
+ /*\r
+ * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
+ * 5. DATE AND TIME SPECIFICATION\r
+ * http://www.ietf.org/rfc/rfc0822.txt\r
+ */\r
+ $format = '%a, %d %m %y %H:%M:%S ';\r
+ $timestamp -= $offset;\r
+ $suffix = 'GMT';\r
+ break;\r
+ case 'iso8601':\r
+ case 'rfc3339':\r
+ /*\r
+ * RFC3339: Date and Time on the Internet: Timestamps\r
+ * 5. Date and Time format\r
+ * http://www.ietf.org/rfc/rfc3339.txt\r
+ */\r
+ $format = '%Y-%m-%dT%H:%M:%S';\r
+ if ( $offset < 0 )\r
+ {\r
+ $suffix = '-';\r
+ $offset = -$offset;\r
+ }\r
+ else\r
+ {\r
+ $suffix = '+';\r
+ }\r
+ $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
+ break;\r
+ case 'utc':\r
+ case 'iso8601UTC':\r
+ case 'rfc3339UTC':\r
+ /*\r
+ * RFC3339: Date and Time on the Internet: Timestamps\r
+ * 5. Date and Time format\r
+ * http://www.ietf.org/rfc/rfc3339.txt\r
+ */\r
+ $timestamp -= $offset;\r
+ $format = '%Y-%m-%dT%H:%M:%SZ';\r
+ $suffix = '';\r
+ break;\r
+ case '':\r
+ $format = '%X %x';\r
+ $offset = '';\r
+ break;\r
+ default:\r
+ $suffix = '';\r
+ break;\r
+ }\r
+ return i18n::strftime($format, $timestamp) . $suffix;\r
+ }\r
+ \r
+ /**\r
+ * i18n::convert_locale_to_old_language_file_name()\r
+ * NOTE: this should be obsoleted near future.\r
+ * \r
+ * @static\r
+ * @param string $target_locale locale name as language_script_region\r
+ * @return string old translation file name\r
+ */\r
+ static public function convert_locale_to_old_language_file_name($target_locale)\r
+ {\r
+ $target_language = '';\r
+ foreach ( self::$lang_refs as $language => $locale )\r
+ {\r
+ if ( preg_match('#-#', $language) )\r
+ {\r
+ if ( $target_locale . '.' . self::$charset == $locale )\r
+ {\r
+ $target_language = $language;\r
+ break;\r
+ }\r
+ }\r
+ else if ( $target_locale == $locale )\r
+ {\r
+ $target_language = $language;\r
+ }\r
+ }\r
+ return $target_language;\r
+ }\r
+ \r
+ /**\r
+ * i18n::convert_old_language_file_name_to_locale()\r
+ * NOTE: this should be obsoleted near future.\r
+ * \r
+ * @static\r
+ * @param string $target_language old translation file name\r
+ * @return string locale name as language_script_region\r
+ */\r
+ static public function convert_old_language_file_name_to_locale($target_language)\r
+ {\r
+ $target_locale = '';\r
+ foreach ( self::$lang_refs as $language => $locale )\r
+ {\r
+ if ( $target_language == $language )\r
+ {\r
+ if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )\r
+ {\r
+ $target_locale = $match[1];\r
+ }\r
+ else\r
+ {\r
+ $target_locale = $locale;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ return $target_locale;\r
+ }\r
+ \r
+ /**\r
+ * i18n::$lang_refs\r
+ * reference table to convert old and new way to name translation files.\r
+ * NOTE: this should be obsoleted as soon as possible.\r
+ * \r
+ * @static\r
+ */\r
+ static private $lang_refs = array(\r
+ "english" => "en_Latn_US",\r
+ "english-utf8" => "en_Latn_US.UTF-8",\r
+ "bulgarian" => "bg_Cyrl_BG",\r
+ "finnish" => "fi_Latn_FU",\r
+ "catalan" => "ca_Latn_ES",\r
+ "french" => "fr_Latn_FR",\r
+ "russian" => "ru_Cyrl_RU",\r
+ "chinese" => "zh_Hans_CN",\r
+ "simchinese" => "zh_Hans_CN",\r
+ "chineseb5" => "zh_Hant_TW",\r
+ "traditional_chinese" => "zh_Hant_TW",\r
+ "galego" => "gl_Latn_ES",\r
+ "german" => "de_Latn_DE",\r
+ "korean-utf" => "ko_Kore_KR.UTF-8",\r
+ "korean-euc-kr" => "ko_Kore_KR.EUC-KR",\r
+ "slovak" => "sk_Latn_SK",\r
+ "czech" => "cs_Latn_CZ",\r
+ "hungarian" => "hu_Latn_HU",\r
+ "latvian" => "lv_Latn_LV",\r
+ "nederlands" => "nl_Latn_NL",\r
+ "italiano" => "it_Latn_IT",\r
+ "persian" => "fa_Arab_IR",\r
+ "spanish" => "es_Latn_ES",\r
+ "spanish-utf8" => "es_Latn_ES.UTF-8",\r
+ "japanese-euc" => "ja_Jpan_JP.EUC-JP",\r
+ "japanese-utf8" => "ja_Jpan_JP.UTF-8",\r
+ "portuguese_brazil" => "pt_Latn_BR"\r
+ );\r
+}\r