3 * i18n class for Nucleus CMS
\r
4 * written by Takashi Sakamoto as of Feb 03, 2012
\r
6 * This includes wrapper functions of iconv and mbstring
\r
7 * for multibyte processing and includes parameters related to locale.
\r
9 * This program is free software; you can redistribute it and/or
\r
10 * modify it under the terms of the GNU General Public License
\r
11 * as published by the Free Software Foundation; either version 2
\r
12 * of the License, or (at your option) any later version.
\r
13 * (see nucleus/documentation/index.html#license for more info)
\r
15 * @license http://nucleuscms.org/license.txt GNU General Public License
\r
16 * @copyright Copyright (C) 2002-2011 The Nucleus Group
\r
17 * @version $Id: i18n.php 1678 2012-02-26 07:31:36Z sakamocchi $
\r
21 static private $mode = FALSE;
\r
23 static private $charset = '';
\r
24 static private $language = '';
\r
25 static private $script = '';
\r
26 static private $region = '';
\r
27 static private $locale_list = array();
\r
28 static private $timezone = 'UTC';
\r
32 * Initializing i18n class
\r
35 * @param string $charset character set
\r
38 static public function init($charset, $dir)
\r
40 /* i18n is already initialized */
\r
46 /* make locale list in this Nucleus CMS */
\r
47 if ( ($handle = opendir($dir)) === FALSE )
\r
51 while ($filename = readdir($handle))
\r
53 if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
\r
55 if ( !in_array($matches[1], self::$locale_list) )
\r
57 self::$locale_list[] = $matches[1];
\r
63 /* set i18n backend and validate character set */
\r
64 if ( extension_loaded('iconv') )
\r
66 /* this is just for checking the charset. */
\r
67 if ( iconv_set_encoding('internal_encoding', $charset)
\r
68 && iconv_set_encoding('output_encoding', $charset)
\r
69 && iconv_set_encoding('internal_encoding', $charset) )
\r
71 self::$charset = $charset;
\r
72 self::$mode = 'iconv';
\r
75 else if ( extension_loaded('mbstring') )
\r
77 /* this is just for checking the charset. */
\r
78 if ( mb_http_output($charset)
\r
79 && mb_internal_encoding($charset)
\r
80 && mb_regex_encoding($charset) )
\r
82 self::$charset = $charset;
\r
83 self::$mode = 'mbstring';
\r
91 * i18n::get_available_locale_list
\r
92 * return available locale list with current charset
\r
96 * @return array available locale list
\r
98 static public function get_available_locale_list()
\r
100 return self::$locale_list;
\r
104 * i18n::get_current_charset
\r
105 * return current charset
\r
109 * @return string $charset current character set
\r
111 static public function get_current_charset()
\r
113 return self::$charset;
\r
118 * Set current locale
\r
121 * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
\r
122 * @link http://www.ietf.org/rfc/rfc5646.txt
\r
123 * @see 2. The Language Tag
\r
126 * @param string $locale
\r
127 * @return bool TRUE/FALSE
\r
130 static public function set_current_locale($locale)
\r
132 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
\r
134 self::$language = $match[1];
\r
135 self::$script = $match[2];
\r
136 self::$region = $match[3];
\r
144 * Get current locale
\r
150 static public function get_current_locale()
\r
152 $elements = array(self::$language, self::$script, self::$region);
\r
153 return implode('_', $elements);
\r
157 * i18n::confirm_default_date_timezone
\r
158 * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
\r
161 * Some private servers are lack of its timezone setting
\r
162 * http://www.php.net/manual/en/function.date-default-timezone-set.php
\r
168 static public function confirm_default_date_timezone()
\r
170 if ( function_exists('date_default_timezone_get')
\r
171 && FALSE !== ($timezone = @date_default_timezone_get()))
\r
173 self::$timezone = $timezone;
\r
175 if (function_exists('date_default_timezone_set')) {
\r
176 @date_default_timezone_set(self::$timezone);
\r
182 * i18n::get_current_date_timezone()
\r
183 * get current timezone
\r
187 * @return $timezone
\r
189 static public function get_date_timezone()
\r
191 return self::$timezone;
\r
196 * character set converter
\r
199 * @param string $string target string binary
\r
200 * @param string $from original character set encoding
\r
201 * @param string $to target character set encoding
\r
202 * @return string converted string
\r
204 static public function convert($string, $from, $to='')
\r
208 $to = self::$charset;
\r
211 if ( self::$mode == 'iconv' )
\r
213 $string = iconv($from, $to.'//TRANSLIT', $string);
\r
215 else if ( self::$mode == 'mbstring' )
\r
217 $string = mb_convert_encoding($string, $to, $from);
\r
219 return (string) $string;
\r
227 * @param string $string target string
\r
228 * @return integer the number of letters
\r
230 static public function strlen($string)
\r
233 if ( self::$mode == 'iconv' )
\r
235 $length = iconv_strlen($string, self::$charset);
\r
237 else if ( self::$mode == 'mbstring' )
\r
239 $length = mb_strlen($string, self::$charset);
\r
243 $length = strlen($string);
\r
245 return (integer) $length;
\r
253 * @param string $haystack string to search
\r
254 * @param string $needle string for search
\r
255 * @param integer $offset the position from which the search should be performed.
\r
256 * @return integer/FALSE the numeric position of the first occurrence of needle in haystack
\r
258 static public function strpos($haystack, $needle, $offset=0)
\r
261 if ( self::$mode == 'iconv' )
\r
263 $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
\r
265 else if ( self::$mode == 'mbstring' )
\r
267 $position = mb_strpos($haystack, $needle, $offset, self::$charset);
\r
271 $position = strpos($haystack, $needle, $offset);
\r
274 if ( $position !== FALSE)
\r
276 $position = (integer) $position;
\r
286 * @param string $haystack string to search
\r
287 * @param string $needle string for search
\r
288 * @return integer/FALSE the numeric position of the last occurrence of needle in haystack
\r
290 static public function strrpos ($haystack, $needle)
\r
293 if ( self::$mode == 'iconv' )
\r
295 $position = iconv_strrpos($haystack, $needle, self::$charset);
\r
297 else if ( self::$mode == 'mbstring' )
\r
299 $position = mb_strrpos($haystack, $needle, 0, self::$charset);
\r
303 $position = strrpos($haystack, $needle, 0);
\r
306 if ( $position !== FALSE)
\r
308 $position = (integer) $position;
\r
318 * @param string $string string to be cut
\r
319 * @param string $start the position of starting
\r
320 * @param integer $length the length to be cut
\r
321 * @return string the extracted part of string
\r
323 static public function substr($string, $start, $length=0)
\r
326 if ( self::$mode == 'iconv' )
\r
328 $return = iconv_substr($string, $start, $length, self::$charset);
\r
330 else if ( self::$mode == 'mbstring' )
\r
332 $return = mb_substr($string, $start, $length, self::$charset);
\r
336 $return = strrpos($string, $start, $length);
\r
338 return (string) $return;
\r
343 * explode function based on multibyte processing with non-pcre regular expressions
\r
345 * NOTE: we SHOULD use preg_split function instead of this,
\r
346 * and I hope this is obsoleted near future...
\r
349 * http://www.php.net/manual/en/function.preg-split.php
\r
352 * @param string $delimiter singlebyte or multibyte delimiter
\r
353 * @param string $target target string
\r
354 * @param integer $limit the number of index for returned array
\r
355 * @return array array splitted by $delimiter
\r
357 static public function explode($delimiter, $target, $limit=0)
\r
360 $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
\r
361 if ( preg_match($preg_delimiter, $target) === 0 )
\r
363 return (array) $target;
\r
365 for ( $count=0; $limit == 0 || $count < $limit; $count++ )
\r
367 $offset = self::strpos($target, $delimiter);
\r
368 if ( $array != array() && $offset == 0 )
\r
370 $array[] = $target;
\r
373 $array[] = self::substr($target, 0, $offset);
\r
374 $length = self::strlen($target) - $offset;
\r
375 $target = self::substr($target, $offset+1, $length);
\r
378 return (array) $array;
\r
383 * strftime function based on multibyte processing
\r
386 * @param string $format format with singlebyte or multibyte
\r
387 * @param timestamp $timestamp UNIX timestamp
\r
388 * @return string formatted timestamp
\r
390 static public function strftime($format, $timestamp='')
\r
394 if ( $timestamp == '' )
\r
396 $timestamp = time();
\r
399 if ( $format == '%%' )
\r
403 else if ( preg_match('#%[^%]#', $format) === 0 )
\r
408 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
\r
409 $elements = preg_split('#,#', $format);
\r
411 foreach ( $elements as $element )
\r
413 if ( preg_match('#(%[^%])#', $element) )
\r
415 $formatted .= strftime($element, $timestamp);
\r
417 else if ( $element == '%%' )
\r
423 $formatted .= $element;
\r
427 return (string) $formatted;
\r
431 * i18n::formatted_datetime()
\r
432 * return formatted datetime string
\r
434 * Date and Time Formats
\r
435 * @link http://www.w3.org/TR/NOTE-datetime
\r
437 * Working with Time Zones
\r
438 * @link http://www.w3.org/TR/timezone/
\r
440 * @param String $format time expression format
\r
441 * @param String $timestamp UNIX timestamp
\r
442 * @param Integer $offset timestamp offset
\r
443 * @return String formatted datetime
\r
445 static public function formatted_datetime($format, $timestamp, $offset=0)
\r
454 * MySQL 5.0 Reference Manual
\r
455 * 10.3.1. The DATE, DATETIME, and TIMESTAMP Types
\r
456 * http://dev.mysql.com/doc/refman/5.0/en/datetime.html
\r
458 $timestamp += $offset;
\r
459 $format = '%Y-%m-%d %H:%M:%S';
\r
464 * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES
\r
465 * 5. DATE AND TIME SPECIFICATION
\r
466 * http://www.ietf.org/rfc/rfc0822.txt
\r
468 $format = '%a, %d %m %y %H:%M:%S ';
\r
472 $offset = -$offset;
\r
479 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
\r
483 * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES
\r
484 * 5. DATE AND TIME SPECIFICATION
\r
485 * http://www.ietf.org/rfc/rfc0822.txt
\r
487 $format = '%a, %d %m %y %H:%M:%S ';
\r
488 $timestamp -= $offset;
\r
494 * RFC3339: Date and Time on the Internet: Timestamps
\r
495 * 5. Date and Time format
\r
496 * http://www.ietf.org/rfc/rfc3339.txt
\r
498 $format = '%Y-%m-%dT%H:%M:%S';
\r
502 $offset = -$offset;
\r
508 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
\r
514 * RFC3339: Date and Time on the Internet: Timestamps
\r
515 * 5. Date and Time format
\r
516 * http://www.ietf.org/rfc/rfc3339.txt
\r
518 $timestamp -= $offset;
\r
519 $format = '%Y-%m-%dT%H:%M:%SZ';
\r
530 return i18n::strftime($format, $timestamp) . $suffix;
\r
534 * i18n::convert_locale_to_old_language_file_name()
\r
535 * NOTE: this should be obsoleted near future.
\r
538 * @param string $target_locale locale name as language_script_region
\r
539 * @return string old translation file name
\r
541 static public function convert_locale_to_old_language_file_name($target_locale)
\r
543 $target_language = '';
\r
544 foreach ( self::$lang_refs as $language => $locale )
\r
546 if ( preg_match('#-#', $language) )
\r
548 if ( $target_locale . '.' . self::$charset == $locale )
\r
550 $target_language = $language;
\r
554 else if ( $target_locale == $locale )
\r
556 $target_language = $language;
\r
559 return $target_language;
\r
563 * i18n::convert_old_language_file_name_to_locale()
\r
564 * NOTE: this should be obsoleted near future.
\r
567 * @param string $target_language old translation file name
\r
568 * @return string locale name as language_script_region
\r
570 static public function convert_old_language_file_name_to_locale($target_language)
\r
572 $target_locale = '';
\r
573 foreach ( self::$lang_refs as $language => $locale )
\r
575 if ( $target_language == $language )
\r
577 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
\r
579 $target_locale = $match[1];
\r
583 $target_locale = $locale;
\r
588 return $target_locale;
\r
593 * reference table to convert old and new way to name translation files.
\r
594 * NOTE: this should be obsoleted as soon as possible.
\r
598 static private $lang_refs = array(
\r
599 "english" => "en_Latn_US",
\r
600 "english-utf8" => "en_Latn_US.UTF-8",
\r
601 "bulgarian" => "bg_Cyrl_BG",
\r
602 "finnish" => "fi_Latn_FU",
\r
603 "catalan" => "ca_Latn_ES",
\r
604 "french" => "fr_Latn_FR",
\r
605 "russian" => "ru_Cyrl_RU",
\r
606 "chinese" => "zh_Hans_CN",
\r
607 "simchinese" => "zh_Hans_CN",
\r
608 "chineseb5" => "zh_Hant_TW",
\r
609 "traditional_chinese" => "zh_Hant_TW",
\r
610 "galego" => "gl_Latn_ES",
\r
611 "german" => "de_Latn_DE",
\r
612 "korean-utf" => "ko_Kore_KR.UTF-8",
\r
613 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
\r
614 "slovak" => "sk_Latn_SK",
\r
615 "czech" => "cs_Latn_CZ",
\r
616 "hungarian" => "hu_Latn_HU",
\r
617 "latvian" => "lv_Latn_LV",
\r
618 "nederlands" => "nl_Latn_NL",
\r
619 "italiano" => "it_Latn_IT",
\r
620 "persian" => "fa_Arab_IR",
\r
621 "spanish" => "es_Latn_ES",
\r
622 "spanish-utf8" => "es_Latn_ES.UTF-8",
\r
623 "japanese-euc" => "ja_Jpan_JP.EUC-JP",
\r
624 "japanese-utf8" => "ja_Jpan_JP.UTF-8",
\r
625 "portuguese_brazil" => "pt_Latn_BR"
\r