3 * i18n class for Nucleus CMS
4 * written by Takashi Sakamoto as of Feb 03, 2012
6 * This includes wrapper functions of iconv and mbstring
7 * and mail function with 7bit characters encoder
8 * for multibyte processing and includes members related to locale.
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 * (see nucleus/documentation/index.html#license for more info)
18 static private $mode = FALSE;
20 static private $charset = '';
21 static private $language = '';
22 static private $script = '';
23 static private $region = '';
24 static private $locale_list = array();
25 static private $timezone = 'UTC';
29 * Initializing i18n class
32 * @param string $charset character set
35 static public function init($charset, $dir)
37 /* i18n is already initialized */
43 /* make locale list in this Nucleus CMS */
44 if ( ($handle = opendir($dir)) === FALSE )
48 while ($filename = readdir($handle))
50 if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
52 if ( !in_array($matches[1], self::$locale_list) )
54 self::$locale_list[] = $matches[1];
60 /* set i18n backend and validate character set */
61 if ( extension_loaded('iconv') )
63 /* this is just for checking the charset. */
64 if ( iconv_set_encoding('internal_encoding', $charset)
65 && iconv_set_encoding('output_encoding', $charset)
66 && iconv_set_encoding('internal_encoding', $charset) )
68 self::$charset = $charset;
69 self::$mode = 'iconv';
72 else if ( extension_loaded('mbstring') )
74 /* this is just for checking the charset. */
75 if ( mb_http_output($charset)
76 && mb_internal_encoding($charset)
77 && mb_regex_encoding($charset) )
79 self::$charset = $charset;
80 self::$mode = 'mbstring';
88 * i18n::get_available_locale_list
89 * return available locale list with current charset
93 * @return array available locale list
95 static public function get_available_locale_list()
97 return self::$locale_list;
101 * i18n::get_current_charset
102 * return current charset
106 * @return string $charset current character set
108 static public function get_current_charset()
110 return self::$charset;
118 * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
119 * @link http://www.ietf.org/rfc/rfc5646.txt
120 * @see 2. The Language Tag
123 * @param string $locale
124 * @return bool TRUE/FALSE
127 static public function set_current_locale($locale)
129 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
131 self::$language = $match[1];
132 self::$script = $match[2];
133 self::$region = $match[3];
147 static public function get_current_locale()
149 $elements = array(self::$language, self::$script, self::$region);
150 return implode('_', $elements);
154 * i18n::confirm_default_date_timezone
155 * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
158 * Some private servers are lack of its timezone setting
159 * http://www.php.net/manual/en/function.date-default-timezone-set.php
165 static public function confirm_default_date_timezone()
167 if ( function_exists('date_default_timezone_get')
168 && FALSE !== ($timezone = @date_default_timezone_get()))
170 self::$timezone = $timezone;
172 if (function_exists('date_default_timezone_set')) {
173 @date_default_timezone_set(self::$timezone);
179 * i18n::get_current_date_timezone()
180 * get current timezone
186 static public function get_date_timezone()
188 return self::$timezone;
193 * character set converter
196 * @param string $string target string binary
197 * @param string $from original character set encoding
198 * @param string $to target character set encoding
199 * @return string converted string
201 static public function convert($string, $from, $to='')
205 $to = self::$charset;
208 if ( self::$mode == 'iconv' )
210 $string = iconv($from, $to.'//TRANSLIT', $string);
212 else if ( self::$mode == 'mbstring' )
214 $string = mb_convert_encoding($string, $to, $from);
216 return (string) $string;
224 * @param string $string target string
225 * @return integer the number of letters
227 static public function strlen($string)
230 if ( self::$mode == 'iconv' )
232 $length = iconv_strlen($string, self::$charset);
234 else if ( self::$mode == 'mbstring' )
236 $length = mb_strlen($string, self::$charset);
240 $length = strlen($string);
242 return (integer) $length;
250 * @param string $haystack string to search
251 * @param string $needle string for search
252 * @param string $offset the position from which the search should be performed.
253 * @return integer/FALSE the numeric position of the first occurrence of needle in haystack
255 static public function strpos($haystack, $needle, $offset=0)
258 if ( self::$mode == 'iconv' )
260 $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
262 else if ( self::$mode == 'mbstring' )
264 $position = mb_strpos($haystack, $needle, $offset, self::$charset);
268 $position = strpos($haystack, $needle, $offset);
271 if ( $position !== FALSE)
273 $position = (integer) $position;
283 * @param string $haystack string to search
284 * @param string $needle string for search
285 * @return integer/FALSE the numeric position of the last occurrence of needle in haystack
287 static public function strrpos ($haystack, $needle)
290 if ( self::$mode == 'iconv' )
292 $position = iconv_strrpos($haystack, $needle, self::$charset);
294 else if ( self::$mode == 'mbstring' )
296 $position = mb_strrpos($haystack, $needle, 0, self::$charset);
300 $position = strrpos($haystack, $needle, 0);
303 if ( $position !== FALSE)
305 $position = (integer) $position;
315 * @param string $string string to be cut
316 * @param string $start the position of starting
317 * @param integer $length the length to be cut
318 * @return string the extracted part of string
320 static public function substr($string, $start, $length=0)
323 if ( self::$mode == 'iconv' )
325 $return = iconv_substr($string, $start, $length, self::$charset);
327 else if ( self::$mode == 'mbstring' )
329 $return = mb_substr($string, $start, $length, self::$charset);
333 $return = strrpos($string, $start, $length);
335 return (string) $return;
340 * explode function based on multibyte processing with non-pcre regular expressions
342 * NOTE: we SHOULD use preg_split function instead of this,
343 * and I hope this is obsoleted near future...
346 * http://www.php.net/manual/en/function.preg-split.php
349 * @param string $delimiter singlebyte or multibyte delimiter
350 * @param string $target target string
351 * @param integer $limit the number of index for returned array
352 * @return array array splitted by $delimiter
354 static public function explode($delimiter, $target, $limit=0)
357 $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
358 if ( preg_match($preg_delimiter, $target) === 0 )
360 return (array) $target;
362 for ( $count=0; $limit == 0 || $count < $limit; $count++ )
364 $offset = self::strpos($target, $delimiter);
365 if ( $array != array() && $offset == 0 )
370 $array[] = self::substr($target, 0, $offset);
371 $length = self::strlen($target) - $offset;
372 $target = self::substr($target, $offset+1, $length);
375 return (array) $array;
380 * strftime function based on multibyte processing
383 * @param string $format format with singlebyte or multibyte
384 * @param timestamp $timestamp UNIX timestamp
385 * @return string formatted timestamp
387 static public function strftime($format, $timestamp='')
391 if ( $timestamp == '' )
396 if ( $format == '%%' )
400 else if ( preg_match('#%[^%]#', $format) === 0 )
405 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
406 $elements = preg_split('#,#', $format);
408 foreach ( $elements as $element )
410 if ( preg_match('#(%[^%])#', $element) )
412 $formatted .= strftime($element, $timestamp);
414 else if ( $element == '%%' )
420 $formatted .= $element;
424 return (string) $formatted;
428 * i18n::formatted_datetime()
429 * return formatted datetime string
431 * Date and Time Formats
432 * @link http://www.w3.org/TR/NOTE-datetime
434 * Working with Time Zones
435 * @link http://www.w3.org/TR/timezone/
437 * @param String $format timezone format
438 * @param String $timestamp UNIX timestamp
439 * @param String $default_format default timezone format
440 * @param Integer $offset timestamp offset
441 * @return String formatted datetime
443 static public function formatted_datetime($format, $timestamp, $default_format, $offset)
451 $format = 'D, j M Y H:i:s ';
462 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
465 $format = 'D, j M Y H:i:s ';
466 $timestamp -= $offset;
470 $timestamp -= $offset;
471 $format = 'Y-m-d\TH:i:s\Z';
475 $format = 'Y-m-d\TH:i:s';
485 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
488 $format = $default_format;
495 return i18n::strftime($format, $timestamp) . $suffix;
499 * i18n::convert_locale_to_old_language_file_name()
500 * NOTE: this should be obsoleted near future.
503 * @param string $target_locale locale name as language_script_region
504 * @return string old translation file name
506 static public function convert_locale_to_old_language_file_name($target_locale)
508 $target_language = '';
509 foreach ( self::$lang_refs as $language => $locale )
511 if ( preg_match('#-#', $language) )
513 if ( $target_locale . '.' . self::$charset == $locale )
515 $target_language = $language;
519 else if ( $target_locale == $locale )
521 $target_language = $language;
524 return $target_language;
528 * i18n::convert_old_language_file_name_to_locale()
529 * NOTE: this should be obsoleted near future.
532 * @param string $target_language old translation file name
533 * @return string locale name as language_script_region
535 static public function convert_old_language_file_name_to_locale($target_language)
538 foreach ( self::$lang_refs as $language => $locale )
540 if ( $target_language == $language )
542 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
544 $target_locale = $match[1];
548 $target_locale = $locale;
553 return $target_locale;
558 * reference table to convert old and new way to name translation files.
559 * NOTE: this should be obsoleted as soon as possible.
563 static private $lang_refs = array(
564 "english" => "en_Latn_US",
565 "english-utf8" => "en_Latn_US.UTF-8",
566 "bulgarian" => "bg_Cyrl_BG",
567 "finnish" => "fi_Latn_FU",
568 "catalan" => "ca_Latn_ES",
569 "french" => "fr_Latn_FR",
570 "russian" => "ru_Cyrl_RU",
571 "chinese" => "zh_Hans_CN",
572 "simchinese" => "zh_Hans_CN",
573 "chineseb5" => "zh_Hant_TW",
574 "traditional_chinese" => "zh_Hant_TW",
575 "galego" => "gl_Latn_ES",
576 "german" => "de_Latn_DE",
577 "korean-utf" => "ko_Kore_KR.UTF-8",
578 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
579 "slovak" => "sk_Latn_SK",
580 "czech" => "cs_Latn_CZ",
581 "hungarian" => "hu_Latn_HU",
582 "latvian" => "lv_Latn_LV",
583 "nederlands" => "nl_Latn_NL",
584 "italiano" => "it_Latn_IT",
585 "persian" => "fa_Arab_IR",
586 "spanish" => "es_Latn_ES",
587 "spanish-utf8" => "es_Latn_ES.UTF-8",
588 "japanese-euc" => "ja_Jpan_JP.EUC-JP",
589 "japanese-utf8" => "ja_Jpan_JP.UTF-8",
590 "portuguese_brazil" => "pt_Latn_BR"