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 * for multibyte processing and includes parameters related to locale.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 * (see nucleus/documentation/index.html#license for more info)
15 * @license http://nucleuscms.org/license.txt GNU General Public License
16 * @copyright Copyright (C) 2002-2011 The Nucleus Group
17 * @version $Id: i18n.php 1673 2012-02-26 04:21:21Z sakamocchi $
21 static private $mode = FALSE;
23 static private $charset = '';
24 static private $language = '';
25 static private $script = '';
26 static private $region = '';
27 static private $locale_list = array();
28 static private $timezone = 'UTC';
32 * Initializing i18n class
35 * @param string $charset character set
38 static public function init($charset, $dir)
40 /* i18n is already initialized */
46 /* make locale list in this Nucleus CMS */
47 if ( ($handle = opendir($dir)) === FALSE )
51 while ($filename = readdir($handle))
53 if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
55 if ( !in_array($matches[1], self::$locale_list) )
57 self::$locale_list[] = $matches[1];
63 /* set i18n backend and validate character set */
64 if ( extension_loaded('iconv') )
66 /* this is just for checking the charset. */
67 if ( iconv_set_encoding('internal_encoding', $charset)
68 && iconv_set_encoding('output_encoding', $charset)
69 && iconv_set_encoding('internal_encoding', $charset) )
71 self::$charset = $charset;
72 self::$mode = 'iconv';
75 else if ( extension_loaded('mbstring') )
77 /* this is just for checking the charset. */
78 if ( mb_http_output($charset)
79 && mb_internal_encoding($charset)
80 && mb_regex_encoding($charset) )
82 self::$charset = $charset;
83 self::$mode = 'mbstring';
91 * i18n::get_available_locale_list
92 * return available locale list with current charset
96 * @return array available locale list
98 static public function get_available_locale_list()
100 return self::$locale_list;
104 * i18n::get_current_charset
105 * return current charset
109 * @return string $charset current character set
111 static public function get_current_charset()
113 return self::$charset;
121 * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
122 * @link http://www.ietf.org/rfc/rfc5646.txt
123 * @see 2. The Language Tag
126 * @param string $locale
127 * @return bool TRUE/FALSE
130 static public function set_current_locale($locale)
132 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
134 self::$language = $match[1];
135 self::$script = $match[2];
136 self::$region = $match[3];
150 static public function get_current_locale()
152 $elements = array(self::$language, self::$script, self::$region);
153 return implode('_', $elements);
157 * i18n::confirm_default_date_timezone
158 * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
161 * Some private servers are lack of its timezone setting
162 * http://www.php.net/manual/en/function.date-default-timezone-set.php
168 static public function confirm_default_date_timezone()
170 if ( function_exists('date_default_timezone_get')
171 && FALSE !== ($timezone = @date_default_timezone_get()))
173 self::$timezone = $timezone;
175 if (function_exists('date_default_timezone_set')) {
176 @date_default_timezone_set(self::$timezone);
182 * i18n::get_current_date_timezone()
183 * get current timezone
189 static public function get_date_timezone()
191 return self::$timezone;
196 * character set converter
199 * @param string $string target string binary
200 * @param string $from original character set encoding
201 * @param string $to target character set encoding
202 * @return string converted string
204 static public function convert($string, $from, $to='')
208 $to = self::$charset;
211 if ( self::$mode == 'iconv' )
213 $string = iconv($from, $to.'//TRANSLIT', $string);
215 else if ( self::$mode == 'mbstring' )
217 $string = mb_convert_encoding($string, $to, $from);
219 return (string) $string;
227 * @param string $string target string
228 * @return integer the number of letters
230 static public function strlen($string)
233 if ( self::$mode == 'iconv' )
235 $length = iconv_strlen($string, self::$charset);
237 else if ( self::$mode == 'mbstring' )
239 $length = mb_strlen($string, self::$charset);
243 $length = strlen($string);
245 return (integer) $length;
253 * @param string $haystack string to search
254 * @param string $needle string for search
255 * @param string $offset the position from which the search should be performed.
256 * @return integer/FALSE the numeric position of the first occurrence of needle in haystack
258 static public function strpos($haystack, $needle, $offset=0)
261 if ( self::$mode == 'iconv' )
263 $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
265 else if ( self::$mode == 'mbstring' )
267 $position = mb_strpos($haystack, $needle, $offset, self::$charset);
271 $position = strpos($haystack, $needle, $offset);
274 if ( $position !== FALSE)
276 $position = (integer) $position;
286 * @param string $haystack string to search
287 * @param string $needle string for search
288 * @return integer/FALSE the numeric position of the last occurrence of needle in haystack
290 static public function strrpos ($haystack, $needle)
293 if ( self::$mode == 'iconv' )
295 $position = iconv_strrpos($haystack, $needle, self::$charset);
297 else if ( self::$mode == 'mbstring' )
299 $position = mb_strrpos($haystack, $needle, 0, self::$charset);
303 $position = strrpos($haystack, $needle, 0);
306 if ( $position !== FALSE)
308 $position = (integer) $position;
318 * @param string $string string to be cut
319 * @param string $start the position of starting
320 * @param integer $length the length to be cut
321 * @return string the extracted part of string
323 static public function substr($string, $start, $length=0)
326 if ( self::$mode == 'iconv' )
328 $return = iconv_substr($string, $start, $length, self::$charset);
330 else if ( self::$mode == 'mbstring' )
332 $return = mb_substr($string, $start, $length, self::$charset);
336 $return = strrpos($string, $start, $length);
338 return (string) $return;
343 * explode function based on multibyte processing with non-pcre regular expressions
345 * NOTE: we SHOULD use preg_split function instead of this,
346 * and I hope this is obsoleted near future...
349 * http://www.php.net/manual/en/function.preg-split.php
352 * @param string $delimiter singlebyte or multibyte delimiter
353 * @param string $target target string
354 * @param integer $limit the number of index for returned array
355 * @return array array splitted by $delimiter
357 static public function explode($delimiter, $target, $limit=0)
360 $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
361 if ( preg_match($preg_delimiter, $target) === 0 )
363 return (array) $target;
365 for ( $count=0; $limit == 0 || $count < $limit; $count++ )
367 $offset = self::strpos($target, $delimiter);
368 if ( $array != array() && $offset == 0 )
373 $array[] = self::substr($target, 0, $offset);
374 $length = self::strlen($target) - $offset;
375 $target = self::substr($target, $offset+1, $length);
378 return (array) $array;
383 * strftime function based on multibyte processing
386 * @param string $format format with singlebyte or multibyte
387 * @param timestamp $timestamp UNIX timestamp
388 * @return string formatted timestamp
390 static public function strftime($format, $timestamp='')
394 if ( $timestamp == '' )
399 if ( $format == '%%' )
403 else if ( preg_match('#%[^%]#', $format) === 0 )
408 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
409 $elements = preg_split('#,#', $format);
411 foreach ( $elements as $element )
413 if ( preg_match('#(%[^%])#', $element) )
415 $formatted .= strftime($element, $timestamp);
417 else if ( $element == '%%' )
423 $formatted .= $element;
427 return (string) $formatted;
431 * i18n::formatted_datetime()
432 * return formatted datetime string
434 * Date and Time Formats
435 * @link http://www.w3.org/TR/NOTE-datetime
437 * Working with Time Zones
438 * @link http://www.w3.org/TR/timezone/
440 * @param String $format timezone format
441 * @param String $timestamp UNIX timestamp
442 * @param String $default_format default timezone format
443 * @param Integer $offset timestamp offset
444 * @return String formatted datetime
446 static public function formatted_datetime($format, $timestamp, $default_format, $offset)
454 $format = 'D, j M Y H:i:s ';
465 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
468 $format = 'D, j M Y H:i:s ';
469 $timestamp -= $offset;
473 $timestamp -= $offset;
474 $format = 'Y-m-d\TH:i:s\Z';
478 $format = 'Y-m-d\TH:i:s';
488 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
491 $format = $default_format;
498 return i18n::strftime($format, $timestamp) . $suffix;
502 * i18n::convert_locale_to_old_language_file_name()
503 * NOTE: this should be obsoleted near future.
506 * @param string $target_locale locale name as language_script_region
507 * @return string old translation file name
509 static public function convert_locale_to_old_language_file_name($target_locale)
511 $target_language = '';
512 foreach ( self::$lang_refs as $language => $locale )
514 if ( preg_match('#-#', $language) )
516 if ( $target_locale . '.' . self::$charset == $locale )
518 $target_language = $language;
522 else if ( $target_locale == $locale )
524 $target_language = $language;
527 return $target_language;
531 * i18n::convert_old_language_file_name_to_locale()
532 * NOTE: this should be obsoleted near future.
535 * @param string $target_language old translation file name
536 * @return string locale name as language_script_region
538 static public function convert_old_language_file_name_to_locale($target_language)
541 foreach ( self::$lang_refs as $language => $locale )
543 if ( $target_language == $language )
545 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
547 $target_locale = $match[1];
551 $target_locale = $locale;
556 return $target_locale;
561 * reference table to convert old and new way to name translation files.
562 * NOTE: this should be obsoleted as soon as possible.
566 static private $lang_refs = array(
567 "english" => "en_Latn_US",
568 "english-utf8" => "en_Latn_US.UTF-8",
569 "bulgarian" => "bg_Cyrl_BG",
570 "finnish" => "fi_Latn_FU",
571 "catalan" => "ca_Latn_ES",
572 "french" => "fr_Latn_FR",
573 "russian" => "ru_Cyrl_RU",
574 "chinese" => "zh_Hans_CN",
575 "simchinese" => "zh_Hans_CN",
576 "chineseb5" => "zh_Hant_TW",
577 "traditional_chinese" => "zh_Hant_TW",
578 "galego" => "gl_Latn_ES",
579 "german" => "de_Latn_DE",
580 "korean-utf" => "ko_Kore_KR.UTF-8",
581 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
582 "slovak" => "sk_Latn_SK",
583 "czech" => "cs_Latn_CZ",
584 "hungarian" => "hu_Latn_HU",
585 "latvian" => "lv_Latn_LV",
586 "nederlands" => "nl_Latn_NL",
587 "italiano" => "it_Latn_IT",
588 "persian" => "fa_Arab_IR",
589 "spanish" => "es_Latn_ES",
590 "spanish-utf8" => "es_Latn_ES.UTF-8",
591 "japanese-euc" => "ja_Jpan_JP.EUC-JP",
592 "japanese-utf8" => "ja_Jpan_JP.UTF-8",
593 "portuguese_brazil" => "pt_Latn_BR"