OSDN Git Service

Merge branch 'skinnable-master'
[nucleus-jp/nucleus-next.git] / nucleus / libs / i18n.php
1 <<<<<<< HEAD
2 <?php\r
3 /**\r
4  * i18n class for Nucleus CMS\r
5  * written by Takashi Sakamoto as of Feb 03, 2012\r
6  * \r
7  * This includes wrapper functions of iconv and mbstring\r
8  * for multibyte processing and includes parameters related to locale.\r
9  * \r
10  * This program is free software; you can redistribute it and/or\r
11  * modify it under the terms of the GNU General Public License\r
12  * as published by the Free Software Foundation; either version 2\r
13  * of the License, or (at your option) any later version.\r
14  * (see nucleus/documentation/index.html#license for more info)\r
15  *\r
16  * @license http://nucleuscms.org/license.txt GNU General Public License\r
17  * @copyright Copyright (C) 2002-2011 The Nucleus Group\r
18  * @version $Id: i18n.php 1678 2012-02-26 07:31:36Z sakamocchi $\r
19  */\r
20 class i18n\r
21 {\r
22         static private $mode = FALSE;\r
23         \r
24         static private $current_charset = '';\r
25         static private $current_language = '';\r
26         static private $current_script = '';\r
27         static private $current_region = '';\r
28         \r
29         static private $locale_list = array();\r
30         static private $timezone = 'UTC';\r
31         \r
32         static private $forced_charset = '';\r
33         static private $forced_language = '';\r
34         static private $forced_script = '';\r
35         static private $forced_region = '';\r
36         \r
37         /**\r
38          * i18n::init\r
39          * Initializing i18n class\r
40          * \r
41          * @static\r
42          * @param       string  $charset        character set\r
43          * @return      boolean \r
44          */\r
45         static public function init($charset, $dir)\r
46         {\r
47                 /* i18n is already initialized */\r
48                 if ( self::$mode )\r
49                 {\r
50                         return TRUE;\r
51                 }\r
52                 \r
53                 /* make locale list in this Nucleus CMS */\r
54                 if ( ($handle = opendir($dir)) === FALSE )\r
55                 {\r
56                         return FALSE;\r
57                 }\r
58                 while ($filename = readdir($handle))\r
59                 {\r
60                         if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )\r
61                         {\r
62                                 if ( !in_array($matches[1], self::$locale_list) )\r
63                                 {\r
64                                         self::$locale_list[] = $matches[1];\r
65                                 }\r
66                         }\r
67                 }\r
68                 closedir($handle);\r
69                 \r
70                 /* set i18n backend and validate character set */\r
71                 if ( extension_loaded('iconv') )\r
72                 {\r
73                         /* this is just for checking the charset. */\r
74                         if ( iconv_set_encoding('internal_encoding', $charset)\r
75                          && iconv_set_encoding('output_encoding', $charset)\r
76                          && iconv_set_encoding('internal_encoding', $charset) )\r
77                         {\r
78                                 self::$current_charset = $charset;\r
79                                 self::$mode = 'iconv';\r
80                         }\r
81                 }\r
82                 else if ( extension_loaded('mbstring') )\r
83                 {\r
84                         /* this is just for checking the charset. */\r
85                         if ( mb_http_output($charset)\r
86                          && mb_internal_encoding($charset)\r
87                          && mb_regex_encoding($charset) )\r
88                         {\r
89                                 self::$current_charset = $charset;\r
90                                 self::$mode = 'mbstring';\r
91                         }\r
92                 }\r
93                 \r
94                 return TRUE;\r
95         }\r
96         \r
97         /**\r
98          * i18n::get_available_locale_list\r
99          * return available locale list with current charset\r
100          * \r
101          * @static\r
102          * @param       void\r
103          * @return      array   available locale list\r
104          */\r
105         static public function get_available_locale_list()\r
106         {\r
107                 return self::$locale_list;\r
108         }\r
109         \r
110         /**\r
111          * i18n::get_current_charset\r
112          * return current charset\r
113          * \r
114          * @static\r
115          * @param       void\r
116          * @return      string  $charset        current character set\r
117          */\r
118         static public function get_current_charset()\r
119         {\r
120                 return self::$current_charset;\r
121         }\r
122         \r
123         /**\r
124          * i18n::set_locale\r
125          * Set current locale\r
126          * \r
127          * NOTE:\r
128          * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.\r
129          * @link http://www.ietf.org/rfc/rfc5646.txt\r
130          * @see 2.  The Language Tag\r
131          * \r
132          * @static\r
133          * @param       string  $locale\r
134          * @return      bool    TRUE/FALSE\r
135          * \r
136          */\r
137         static public function set_current_locale($locale)\r
138         {\r
139                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )\r
140                 {\r
141                         self::$current_language = $match[1];\r
142                         self::$current_script   = $match[2];\r
143                         self::$current_region   = $match[3];\r
144                         return TRUE;\r
145                 }\r
146                 return FALSE;\r
147         }\r
148         \r
149         /**\r
150          * i18n::get_locale\r
151          * Get current locale\r
152          * \r
153          * @static\r
154          * @param       void\r
155          * @return      $locale\r
156          */\r
157         static public function get_current_locale()\r
158         {\r
159                 $elements = array(self::$current_language, self::$current_script, self::$current_region);\r
160                 return implode('_', $elements);\r
161         }\r
162         \r
163         /**\r
164          * i18n::set_forced_locale()\r
165          * Set forced locale\r
166          * \r
167          * @static\r
168          * @param       string  $forced_locale\r
169          * @return      bool    TRUE/FALSE\r
170          * \r
171          */\r
172         static public function set_forced_locale($forced_locale)\r
173         {\r
174                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $forced_locale, $match) )\r
175                 {\r
176                         self::$forced_language  = $match[1];\r
177                         self::$forced_script    = $match[2];\r
178                         self::$forced_region    = $match[3];\r
179                         return TRUE;\r
180                 }\r
181                 return FALSE;\r
182         }\r
183         \r
184         /**\r
185          * i18n::get_forced_locale\r
186          * Get forced locale\r
187          * \r
188          * @static\r
189          * @param       void\r
190          * @return      $forced_locale\r
191          */\r
192         static public function get_forced_locale()\r
193         {\r
194                 if ( !self::$forced_language )\r
195                 {\r
196                         return '';\r
197                 }\r
198                 \r
199                 $elements = array(self::$forced_language, self::$forced_script, self::$forced_region);\r
200                 return implode('_', $elements);\r
201         }\r
202         \r
203         /**\r
204          * i18n::set_forced_charset\r
205          * return forced charset\r
206          * \r
207          * @static\r
208          * @param       void    $charset        forced character set\r
209          * @return      void\r
210          */\r
211         static public function set_forced_charset($forced_charset)\r
212         {\r
213                 self::$forced_charset = $forced_charset;\r
214                 return;\r
215         }\r
216         \r
217         /**\r
218          * i18n::get_forced_charset\r
219          * return forced charset\r
220          * \r
221          * @static\r
222          * @param       void\r
223          * @return      string  $charset        forced character set\r
224          */\r
225         static public function get_forced_charset()\r
226         {\r
227                 return self::$forced_charset;\r
228         }\r
229         \r
230         /**\r
231          * i18n::confirm_default_date_timezone\r
232          * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.\r
233          * \r
234          * NOTE:\r
235          * Some private servers are lack of its timezone setting\r
236          * http://www.php.net/manual/en/function.date-default-timezone-set.php\r
237          * \r
238          * @static\r
239          * @param       void\r
240          * @return      void\r
241          */\r
242         static public function confirm_default_date_timezone()\r
243         {\r
244                 if ( function_exists('date_default_timezone_get') \r
245                  && FALSE !== ($timezone = @date_default_timezone_get()))\r
246                 {\r
247                         self::$timezone = $timezone;\r
248                 }\r
249                 if (function_exists('date_default_timezone_set')) {\r
250                          @date_default_timezone_set(self::$timezone);\r
251                 }\r
252                 return;\r
253         }\r
254         \r
255         /**\r
256          * i18n::get_current_date_timezone()\r
257          * get current timezone\r
258          * \r
259          * @static\r
260          * @param       void\r
261          * @return      $timezone\r
262          */\r
263         static public function get_date_timezone()\r
264         {\r
265                 return self::$timezone;\r
266         }\r
267         \r
268         /**\r
269          * i18n::convert\r
270          * character set converter\r
271          * \r
272          * @static\r
273          * @param       string  $string target string binary\r
274          * @param       string  $from   original character set encoding\r
275          * @param       string  $to     target character set encoding\r
276          * @return      string  converted string\r
277          */\r
278         static public function convert($string, $from, $to='')\r
279         {\r
280                 if ( $to == '' )\r
281                 {\r
282                         $to = self::$current_charset;\r
283                 }\r
284                 \r
285                 if ( $from == $to )\r
286                 {\r
287                         /* do nothing */\r
288                 }\r
289                 else if ( self::$mode == 'iconv' )\r
290                 {\r
291                         $string = iconv($from, $to.'//TRANSLIT', $string);\r
292                 }\r
293                 else if ( self::$mode == 'mbstring' )\r
294                 {\r
295                         $string = mb_convert_encoding($string, $to, $from);\r
296                 }\r
297                 return (string) $string;\r
298         }\r
299         \r
300         /**\r
301          * i18n::convert_handler\r
302          * callable handler for character set converter\r
303          * \r
304          * @static\r
305          * @param       string  $string target string binary\r
306          * @return      void\r
307          */\r
308         static public function convert_handler($string)\r
309         {\r
310                 return self::convert($string, self::$current_charset, self::$forced_charset);\r
311         }\r
312         \r
313         /**\r
314          * i18n::convert_array\r
315          * recursively converting array\r
316          * \r
317          * @static\r
318          * @param       array   $array  array to convert\r
319          * @return      void\r
320          */\r
321         static public function convert_array($array, $from, $to='')\r
322         {\r
323                 if ( !is_array($array) )\r
324                 {\r
325                         $array = self::convert($array, $from, $to);\r
326                 }\r
327                 else\r
328                 {\r
329                         foreach ( $array as $key => $value )\r
330                         {\r
331                                 if ( !is_array($value) )\r
332                                 {\r
333                                         $array[$key] = self::convert($value, $from, $to);\r
334                                 }\r
335                                 else\r
336                                 {\r
337                                         self::convert_array($array[$key]);\r
338                                 }\r
339                         }\r
340                 }\r
341                 \r
342                 return $array;\r
343         }\r
344         \r
345         /**\r
346          * i18n::strlen\r
347          * strlen wrapper\r
348          * \r
349          * @static\r
350          * @param       string  $string target string\r
351          * @return      integer the number of letters\r
352          */\r
353         static public function strlen($string)\r
354         {\r
355                 $length = 0;\r
356                 if ( self::$mode == 'iconv' )\r
357                 {\r
358                         $length = iconv_strlen($string, self::$current_charset);\r
359                 }\r
360                 else if ( self::$mode == 'mbstring' )\r
361                 {\r
362                         $length = mb_strlen($string, self::$current_charset);\r
363                 }\r
364                 else\r
365                 {\r
366                         $length = strlen($string);\r
367                 }\r
368                 return (integer) $length;\r
369         }\r
370         \r
371         /**\r
372          * i18n::strpos\r
373          * strpos wrapper\r
374          * \r
375          * @static\r
376          * @param       string  $haystack       string to search\r
377          * @param       string  $needle string for search\r
378          * @param       integer $offset the position from which the search should be performed. \r
379          * @return      integer/FALSE   the numeric position of the first occurrence of needle in haystack\r
380          */\r
381         static public function strpos($haystack, $needle, $offset=0)\r
382         {\r
383                 $position = 0;\r
384                 if ( self::$mode == 'iconv' )\r
385                 {\r
386                         $position = iconv_strpos($haystack, $needle, $offset, self::$current_charset);\r
387                 }\r
388                 else if ( self::$mode == 'mbstring' )\r
389                 {\r
390                         $position = mb_strpos($haystack, $needle, $offset, self::$current_charset);\r
391                 }\r
392                 else\r
393                 {\r
394                         $position = strpos($haystack, $needle, $offset);\r
395                 }\r
396                 \r
397                 if ( $position !== FALSE)\r
398                 {\r
399                         $position = (integer) $position;\r
400                 }\r
401                 return $position;\r
402         }\r
403         \r
404         /**\r
405          * i18n::strrpos\r
406          * strrpos wrapper\r
407          * \r
408          * @static\r
409          * @param       string  $haystack       string to search\r
410          * @param       string  $needle string for search\r
411          * @return      integer/FALSE   the numeric position of the last occurrence of needle in haystack\r
412          */\r
413         static public function strrpos ($haystack, $needle)\r
414         {\r
415                 $position = 0;\r
416                 if ( self::$mode == 'iconv' )\r
417                 {\r
418                         $position = iconv_strrpos($haystack, $needle, self::$current_charset);\r
419                 }\r
420                 else if ( self::$mode == 'mbstring' )\r
421                 {\r
422                         $position = mb_strrpos($haystack, $needle, 0, self::$current_charset);\r
423                 }\r
424                 else\r
425                 {\r
426                         $position = strrpos($haystack, $needle, 0);\r
427                 }\r
428                 \r
429                 if ( $position !== FALSE)\r
430                 {\r
431                         $position = (integer) $position;\r
432                 }\r
433                 return $position;\r
434         }\r
435         \r
436         /**\r
437          * i18n::substr\r
438          * substr wrapper\r
439          * \r
440          * @static\r
441          * @param       string  $string string to be cut\r
442          * @param       string  $start  the position of starting\r
443          * @param       integer $length the length to be cut\r
444          * @return      string  the extracted part of string\r
445          */\r
446         static public function substr($string, $start, $length=0)\r
447         {\r
448                 $return = '';\r
449                 \r
450                 if ( $length == 0 )\r
451                 {\r
452                         $length = self::strlen($string) - $start;\r
453                 }\r
454                 \r
455                 if ( self::$mode == 'iconv' )\r
456                 {\r
457                         $return = iconv_substr($string, $start, $length, self::$current_charset);\r
458                 }\r
459                 else if ( self::$mode == 'mbstring' )\r
460                 {\r
461                         $return = mb_substr($string, $start, $length, self::$current_charset);\r
462                 }\r
463                 else\r
464                 {\r
465                         $return = strrpos($string, $start, $length);\r
466                 }\r
467                 return (string) $return;\r
468         }\r
469         \r
470         /**\r
471          * i18n::strftime\r
472          * strftime function based on multibyte processing\r
473          * \r
474          * @static\r
475          * @param       string  $format format with singlebyte or multibyte\r
476          * @param       timestamp       $timestamp      UNIX timestamp\r
477          * @return      string  formatted timestamp\r
478          */\r
479         static public function strftime($format, $timestamp='')\r
480         {\r
481                 return preg_replace_callback('/(%[a-z%])/i',\r
482                         create_function('$matches', 'return strftime($matches[1], ' . intval($timestamp) . ');'),\r
483                         $format\r
484                 );\r
485         }\r
486         \r
487         /**\r
488          * i18n::formatted_datetime()\r
489          * return formatted datetime string\r
490          * \r
491          * Date and Time Formats\r
492          * @link        http://www.w3.org/TR/NOTE-datetime\r
493          * \r
494          * Working with Time Zones\r
495          * @link        http://www.w3.org/TR/timezone/\r
496          * \r
497          * @param       String  $format time expression format\r
498          * @param       String  $timestamp      UNIX timestamp\r
499          * @param       Integer $offset timestamp offset\r
500          * @return      String  formatted datetime\r
501          */\r
502         static public function formatted_datetime($format, $timestamp, $offset=0)\r
503         {\r
504                 $suffix = '';\r
505                 $string = '';\r
506                 \r
507                 switch ( $format )\r
508                 {\r
509                         case 'mysql':\r
510                                 /*\r
511                                  * MySQL 5.0 Reference Manual\r
512                                  *  10.3.1. The DATE, DATETIME, and TIMESTAMP Types\r
513                                  *   http://dev.mysql.com/doc/refman/5.0/en/datetime.html\r
514                                  */\r
515                                 $timestamp += $offset;\r
516                                 $format = '%Y-%m-%d %H:%M:%S';\r
517                                 $suffix ='';\r
518                                 break;\r
519                         case 'rfc822':\r
520                                 /*\r
521                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
522                                  *  5.  DATE AND TIME SPECIFICATION\r
523                                  *   http://www.ietf.org/rfc/rfc0822.txt\r
524                                  */\r
525                                 $format = '%a, %d %m %y %H:%M:%S ';\r
526                                 if ( $offset < 0 )\r
527                                 {\r
528                                         $suffix = '-';\r
529                                         $offset = -$offset;\r
530                                 }\r
531                                 else\r
532                                 {\r
533                                         $suffix = '+';\r
534                                 }\r
535                                 \r
536                                 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
537                                 break;\r
538                         case 'rfc822GMT':\r
539                                 /*\r
540                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
541                                  *  5.  DATE AND TIME SPECIFICATION\r
542                                  *   http://www.ietf.org/rfc/rfc0822.txt\r
543                                  */\r
544                                 $format = '%a, %d %m %y %H:%M:%S ';\r
545                                 $timestamp -= $offset;\r
546                                 $suffix = 'GMT';\r
547                                 break;\r
548                         case 'iso8601':\r
549                         case 'rfc3339':\r
550                                 /*\r
551                                  * RFC3339: Date and Time on the Internet: Timestamps\r
552                                  *  5. Date and Time format\r
553                                  *   http://www.ietf.org/rfc/rfc3339.txt\r
554                                  */\r
555                                 $format = '%Y-%m-%dT%H:%M:%S';\r
556                                 if ( $offset < 0 )\r
557                                 {\r
558                                         $suffix = '-';\r
559                                         $offset = -$offset;\r
560                                 }\r
561                                 else\r
562                                 {\r
563                                         $suffix = '+';\r
564                                 }\r
565                                 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
566                                 break;\r
567                         case 'utc':\r
568                         case 'iso8601UTC':\r
569                         case 'rfc3339UTC':\r
570                                 /*\r
571                                  * RFC3339: Date and Time on the Internet: Timestamps\r
572                                  *  5. Date and Time format\r
573                                  *   http://www.ietf.org/rfc/rfc3339.txt\r
574                                  */\r
575                                 $timestamp -= $offset;\r
576                                 $format = '%Y-%m-%dT%H:%M:%SZ';\r
577                                 $suffix = '';\r
578                                 break;\r
579                         case '':\r
580                                 $format = '%X %x';\r
581                                 $offset = '';\r
582                                 break;\r
583                         default:\r
584                                 $suffix = '';\r
585                                 break;\r
586                 }\r
587                 return i18n::strftime($format, $timestamp) . $suffix;\r
588         }\r
589         \r
590         /**\r
591          * i18n::convert_locale_to_old_language_file_name()\r
592          * NOTE: this should be obsoleted near future.\r
593          * \r
594          * @static\r
595          * @param       string  $target_locale  locale name as language_script_region\r
596          * @return      string  old translation file name\r
597          */\r
598         static public function convert_locale_to_old_language_file_name($target_locale)\r
599         {\r
600                 $target_language = '';\r
601                 foreach ( self::$lang_refs as $language => $locale )\r
602                 {\r
603                         if ( preg_match('#-#', $language) )\r
604                         {\r
605                                 if ( $target_locale . '.' . self::$current_charset == $locale )\r
606                                 {\r
607                                         $target_language = $language;\r
608                                         break;\r
609                                 }\r
610                         }\r
611                         else if ( $target_locale == $locale )\r
612                         {\r
613                                 $target_language = $language;\r
614                         }\r
615                 }\r
616                 return $target_language;\r
617         }\r
618         \r
619         /**\r
620          * i18n::convert_old_language_file_name_to_locale()\r
621          * NOTE: this should be obsoleted near future.\r
622          * \r
623          * @static\r
624          * @param       string  $target_language        old translation file name\r
625          * @return      string  locale name as language_script_region\r
626          */\r
627         static public function convert_old_language_file_name_to_locale($target_language)\r
628         {\r
629                 $target_locale = '';\r
630                 foreach ( self::$lang_refs as $language => $locale )\r
631                 {\r
632                         if ( $target_language == $language )\r
633                         {\r
634                                 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )\r
635                                 {\r
636                                         $target_locale = $match[1];\r
637                                 }\r
638                                 else\r
639                                 {\r
640                                         $target_locale = $locale;\r
641                                 }\r
642                                 break;\r
643                         }\r
644                 }\r
645                 return $target_locale;\r
646         }\r
647         \r
648         /**\r
649          * i18n::$lang_refs\r
650          * reference table to convert old and new way to name translation files.\r
651          * NOTE: this should be obsoleted as soon as possible.\r
652          * \r
653          * @static\r
654          */\r
655         static private $lang_refs = array(\r
656                 "english"               => "en_Latn_US",\r
657                 "english-utf8"  => "en_Latn_US.UTF-8",\r
658                 "bulgarian"     => "bg_Cyrl_BG",\r
659                 "finnish"               => "fi_Latn_FU",\r
660                 "catalan"               => "ca_Latn_ES",\r
661                 "french"                => "fr_Latn_FR",\r
662                 "russian"               => "ru_Cyrl_RU",\r
663                 "chinese"               => "zh_Hans_CN",\r
664                 "simchinese"    => "zh_Hans_CN",\r
665                 "chineseb5"     => "zh_Hant_TW",\r
666                 "traditional_chinese"   =>      "zh_Hant_TW",\r
667                 "galego"                => "gl_Latn_ES",\r
668                 "german"                => "de_Latn_DE",\r
669                 "korean-utf"    => "ko_Kore_KR.UTF-8",\r
670                 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",\r
671                 "slovak"                => "sk_Latn_SK",\r
672                 "czech"         => "cs_Latn_CZ",\r
673                 "hungarian"     => "hu_Latn_HU",\r
674                 "latvian"               => "lv_Latn_LV",\r
675                 "nederlands"    => "nl_Latn_NL",\r
676                 "italiano"              => "it_Latn_IT",\r
677                 "persian"               => "fa_Arab_IR",\r
678                 "spanish"               => "es_Latn_ES",\r
679                 "spanish-utf8"  => "es_Latn_ES.UTF-8",\r
680                 "japanese-euc"  => "ja_Jpan_JP.EUC-JP",\r
681                 "japanese-utf8" => "ja_Jpan_JP.UTF-8",\r
682                 "portuguese_brazil"     => "pt_Latn_BR"\r
683         );\r
684 }\r
685 =======
686 <?php
687 /**
688  * i18n class for Nucleus CMS
689  * written by Takashi Sakamoto as of Feb 03, 2012
690  * 
691  * This includes wrapper functions of iconv and mbstring
692  * for multibyte processing and includes parameters related to locale.
693  * 
694  * This program is free software; you can redistribute it and/or
695  * modify it under the terms of the GNU General Public License
696  * as published by the Free Software Foundation; either version 2
697  * of the License, or (at your option) any later version.
698  * (see nucleus/documentation/index.html#license for more info)
699  *
700  * @license http://nucleuscms.org/license.txt GNU General Public License
701  * @copyright Copyright (C) 2002-2011 The Nucleus Group
702  * @version $Id: i18n.php 1876 2012-06-17 07:33:00Z sakamocchi $
703  */
704 class i18n
705 {
706         static private $mode = FALSE;
707         
708         static private $current_charset = '';
709         static private $current_language = '';
710         static private $current_script = '';
711         static private $current_region = '';
712         
713         static private $locale_list = array();
714         static private $timezone = 'UTC';
715         
716         static private $forced_charset = '';
717         static private $forced_language = '';
718         static private $forced_script = '';
719         static private $forced_region = '';
720         
721         /**
722          * i18n::init
723          * Initializing i18n class
724          * 
725          * @static
726          * @param       string  $charset        character set
727          * @return      boolean 
728          */
729         static public function init($charset, $dir)
730         {
731                 /* i18n is already initialized */
732                 if ( self::$mode )
733                 {
734                         return TRUE;
735                 }
736                 
737                 /* make locale list in this Nucleus CMS */
738                 if ( ($handle = opendir($dir)) === FALSE )
739                 {
740                         return FALSE;
741                 }
742                 while ($filename = readdir($handle))
743                 {
744                         if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
745                         {
746                                 if ( !in_array($matches[1], self::$locale_list) )
747                                 {
748                                         self::$locale_list[] = $matches[1];
749                                 }
750                         }
751                 }
752                 closedir($handle);
753                 
754                 /* set i18n backend and validate character set */
755                 if ( extension_loaded('iconv') )
756                 {
757                         /* this is just for checking the charset. */
758                         if ( iconv_set_encoding('internal_encoding', $charset)
759                          && iconv_set_encoding('output_encoding', $charset)
760                          && iconv_set_encoding('internal_encoding', $charset) )
761                         {
762                                 self::$current_charset = $charset;
763                                 self::$mode = 'iconv';
764                         }
765                 }
766                 else if ( extension_loaded('mbstring') )
767                 {
768                         /* this is just for checking the charset. */
769                         if ( mb_http_output($charset)
770                          && mb_internal_encoding($charset)
771                          && mb_regex_encoding($charset) )
772                         {
773                                 self::$current_charset = $charset;
774                                 self::$mode = 'mbstring';
775                         }
776                 }
777                 
778                 return TRUE;
779         }
780         
781         /**
782          * i18n::get_available_locale_list
783          * return available locale list with current charset
784          * 
785          * @static
786          * @param       void
787          * @return      array   available locale list
788          */
789         static public function get_available_locale_list()
790         {
791                 return self::$locale_list;
792         }
793         
794         /**
795          * i18n::get_current_charset
796          * return current charset
797          * 
798          * @static
799          * @param       void
800          * @return      string  $charset        current character set
801          */
802         static public function get_current_charset()
803         {
804                 return self::$current_charset;
805         }
806         
807         /**
808          * i18n::set_locale
809          * Set current locale
810          * 
811          * NOTE:
812          * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
813          * @link http://www.ietf.org/rfc/rfc5646.txt
814          * @see 2.  The Language Tag
815          * 
816          * @static
817          * @param       string  $locale
818          * @return      bool    TRUE/FALSE
819          * 
820          */
821         static public function set_current_locale($locale)
822         {
823                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
824                 {
825                         self::$current_language = $match[1];
826                         self::$current_script   = $match[2];
827                         self::$current_region   = $match[3];
828                         return TRUE;
829                 }
830                 return FALSE;
831         }
832         
833         /**
834          * i18n::get_locale
835          * Get current locale
836          * 
837          * @static
838          * @param       void
839          * @return      $locale
840          */
841         static public function get_current_locale()
842         {
843                 $elements = array(self::$current_language, self::$current_script, self::$current_region);
844                 return implode('_', $elements);
845         }
846         
847         /**
848          * i18n::set_forced_locale()
849          * Set forced locale
850          * 
851          * @static
852          * @param       string  $forced_locale
853          * @return      bool    TRUE/FALSE
854          * 
855          */
856         static public function set_forced_locale($forced_locale)
857         {
858                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $forced_locale, $match) )
859                 {
860                         self::$forced_language  = $match[1];
861                         self::$forced_script    = $match[2];
862                         self::$forced_region    = $match[3];
863                         return TRUE;
864                 }
865                 return FALSE;
866         }
867         
868         /**
869          * i18n::get_forced_locale
870          * Get forced locale
871          * 
872          * @static
873          * @param       void
874          * @return      $forced_locale
875          */
876         static public function get_forced_locale()
877         {
878                 if ( !self::$forced_language )
879                 {
880                         return '';
881                 }
882                 
883                 $elements = array(self::$forced_language, self::$forced_script, self::$forced_region);
884                 return implode('_', $elements);
885         }
886         
887         /**
888          * i18n::set_forced_charset
889          * return forced charset
890          * 
891          * @static
892          * @param       void    $charset        forced character set
893          * @return      void
894          */
895         static public function set_forced_charset($forced_charset)
896         {
897                 self::$forced_charset = $forced_charset;
898                 return;
899         }
900         
901         /**
902          * i18n::get_forced_charset
903          * return forced charset
904          * 
905          * @static
906          * @param       void
907          * @return      string  $charset        forced character set
908          */
909         static public function get_forced_charset()
910         {
911                 return self::$forced_charset;
912         }
913         
914         /**
915          * i18n::confirm_default_date_timezone
916          * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
917          * 
918          * NOTE:
919          * Some private servers are lack of its timezone setting
920          * http://www.php.net/manual/en/function.date-default-timezone-set.php
921          * 
922          * @static
923          * @param       void
924          * @return      void
925          */
926         static public function confirm_default_date_timezone()
927         {
928                 if ( function_exists('date_default_timezone_get') 
929                  && FALSE !== ($timezone = @date_default_timezone_get()))
930                 {
931                         self::$timezone = $timezone;
932                 }
933                 if (function_exists('date_default_timezone_set')) {
934                          @date_default_timezone_set(self::$timezone);
935                 }
936                 return;
937         }
938         
939         /**
940          * i18n::get_current_date_timezone()
941          * get current timezone
942          * 
943          * @static
944          * @param       void
945          * @return      $timezone
946          */
947         static public function get_date_timezone()
948         {
949                 return self::$timezone;
950         }
951         
952         /**
953          * i18n::convert
954          * character set converter
955          * 
956          * @static
957          * @param       string  $string target string binary
958          * @param       string  $from   original character set encoding
959          * @param       string  $to     target character set encoding
960          * @return      string  converted string
961          */
962         static public function convert($string, $from, $to='')
963         {
964                 if ( $to == '' )
965                 {
966                         $to = self::$current_charset;
967                 }
968                 
969                 if ( $from == $to )
970                 {
971                         /* do nothing */
972                 }
973                 else if ( self::$mode == 'iconv' )
974                 {
975                         $string = iconv($from, $to.'//TRANSLIT', $string);
976                 }
977                 else if ( self::$mode == 'mbstring' )
978                 {
979                         $string = mb_convert_encoding($string, $to, $from);
980                 }
981                 return (string) $string;
982         }
983         
984         /**
985          * i18n::convert_handler
986          * callable handler for character set converter
987          * 
988          * @static
989          * @param       string  $string target string binary
990          * @return      void
991          */
992         static public function convert_handler($string)
993         {
994                 return self::convert($string, self::$current_charset, self::$forced_charset);
995         }
996         
997         /**
998          * i18n::convert_array
999          * recursively converting array
1000          * 
1001          * @static
1002          * @param       array   $array  array to convert
1003          * @return      void
1004          */
1005         static public function convert_array($array, $from, $to='')
1006         {
1007                 if ( !is_array($array) )
1008                 {
1009                         $array = self::convert($array, $from, $to);
1010                 }
1011                 else
1012                 {
1013                         foreach ( $array as $key => $value )
1014                         {
1015                                 if ( !is_array($value) )
1016                                 {
1017                                         $array[$key] = self::convert($value, $from, $to);
1018                                 }
1019                                 else
1020                                 {
1021                                         self::convert_array($array[$key]);
1022                                 }
1023                         }
1024                 }
1025                 
1026                 return $array;
1027         }
1028         
1029         /**
1030          * i18n::strlen
1031          * strlen wrapper
1032          * 
1033          * @static
1034          * @param       string  $string target string
1035          * @return      integer the number of letters
1036          */
1037         static public function strlen($string)
1038         {
1039                 $length = 0;
1040                 if ( self::$mode == 'iconv' )
1041                 {
1042                         $length = iconv_strlen($string, self::$current_charset);
1043                 }
1044                 else if ( self::$mode == 'mbstring' )
1045                 {
1046                         $length = mb_strlen($string, self::$current_charset);
1047                 }
1048                 else
1049                 {
1050                         $length = strlen($string);
1051                 }
1052                 return (integer) $length;
1053         }
1054         
1055         /**
1056          * i18n::strpos
1057          * strpos wrapper
1058          * 
1059          * @static
1060          * @param       string  $haystack       string to search
1061          * @param       string  $needle string for search
1062          * @param       integer $offset the position from which the search should be performed. 
1063          * @return      integer/FALSE   the numeric position of the first occurrence of needle in haystack
1064          */
1065         static public function strpos($haystack, $needle, $offset=0)
1066         {
1067                 $position = 0;
1068                 if ( self::$mode == 'iconv' )
1069                 {
1070                         $position = iconv_strpos($haystack, $needle, $offset, self::$current_charset);
1071                 }
1072                 else if ( self::$mode == 'mbstring' )
1073                 {
1074                         $position = mb_strpos($haystack, $needle, $offset, self::$current_charset);
1075                 }
1076                 else
1077                 {
1078                         $position = strpos($haystack, $needle, $offset);
1079                 }
1080                 
1081                 if ( $position !== FALSE)
1082                 {
1083                         $position = (integer) $position;
1084                 }
1085                 return $position;
1086         }
1087         
1088         /**
1089          * i18n::strrpos
1090          * strrpos wrapper
1091          * 
1092          * @static
1093          * @param       string  $haystack       string to search
1094          * @param       string  $needle string for search
1095          * @return      integer/FALSE   the numeric position of the last occurrence of needle in haystack
1096          */
1097         static public function strrpos ($haystack, $needle)
1098         {
1099                 $position = 0;
1100                 if ( self::$mode == 'iconv' )
1101                 {
1102                         $position = iconv_strrpos($haystack, $needle, self::$current_charset);
1103                 }
1104                 else if ( self::$mode == 'mbstring' )
1105                 {
1106                         $position = mb_strrpos($haystack, $needle, 0, self::$current_charset);
1107                 }
1108                 else
1109                 {
1110                         $position = strrpos($haystack, $needle, 0);
1111                 }
1112                 
1113                 if ( $position !== FALSE)
1114                 {
1115                         $position = (integer) $position;
1116                 }
1117                 return $position;
1118         }
1119         
1120         /**
1121          * i18n::substr
1122          * substr wrapper
1123          * 
1124          * @static
1125          * @param       string  $string string to be cut
1126          * @param       string  $start  the position of starting
1127          * @param       integer $length the length to be cut
1128          * @return      string  the extracted part of string
1129          */
1130         static public function substr($string, $start, $length=0)
1131         {
1132                 $return = '';
1133                 
1134                 if ( $length == 0 )
1135                 {
1136                         $length = self::strlen($string) - $start;
1137                 }
1138                 
1139                 if ( self::$mode == 'iconv' )
1140                 {
1141                         $return = iconv_substr($string, $start, $length, self::$current_charset);
1142                 }
1143                 else if ( self::$mode == 'mbstring' )
1144                 {
1145                         $return = mb_substr($string, $start, $length, self::$current_charset);
1146                 }
1147                 else
1148                 {
1149                         $return = strrpos($string, $start, $length);
1150                 }
1151                 return (string) $return;
1152         }
1153         
1154         /**
1155          * i18n::strftime
1156          * strftime function based on multibyte processing
1157          * 
1158          * @static
1159          * @param       string  $format format with singlebyte or multibyte
1160          * @param       timestamp       $timestamp      UNIX timestamp
1161          * @return      string  formatted timestamp
1162          */
1163         static public function strftime($format, $timestamp='')
1164         {
1165                 return preg_replace_callback('/(%[a-z%])/i',
1166                         create_function('$matches', 'return strftime($matches[1], ' . intval($timestamp) . ');'),
1167                         $format
1168                 );
1169                 }
1170                 
1171         /**
1172          * i18n::formatted_datetime()
1173          * return formatted datetime string
1174          * 
1175          * Date and Time Formats
1176          * @link        http://www.w3.org/TR/NOTE-datetime
1177          * 
1178          * Working with Time Zones
1179          * @link        http://www.w3.org/TR/timezone/
1180          * 
1181          * @param       String  $format time expression format
1182          * @param       String  $timestamp      UNIX timestamp
1183          * @param       Integer $offset timestamp offset
1184          * @return      String  formatted datetime
1185          */
1186         static public function formatted_datetime($format, $timestamp, $offset=0)
1187         {
1188                 $suffix = '';
1189                 $string = '';
1190                 
1191                 switch ( $format )
1192                 {
1193                         case 'mysql':
1194                                 /*
1195                                  * MySQL 5.0 Reference Manual
1196                                  *  10.3.1. The DATE, DATETIME, and TIMESTAMP Types
1197                                  *   http://dev.mysql.com/doc/refman/5.0/en/datetime.html
1198                                  */
1199                                 $timestamp += $offset;
1200                                 $format = '%Y-%m-%d %H:%M:%S';
1201                                 $suffix ='';
1202                                 break;
1203                         
1204                         case 'rfc822':
1205                                 /*
1206                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES
1207                                  *  5.  DATE AND TIME SPECIFICATION
1208                                  *   http://www.ietf.org/rfc/rfc0822.txt
1209                                  */
1210                                 $format = '%a, %d %m %y %H:%M:%S ';
1211                                 if ( $offset < 0 )
1212                                 {
1213                                         $suffix = '-';
1214                                         $offset = -$offset;
1215                                 }
1216                                 else
1217                                 {
1218                                         $suffix = '+';
1219                                 }
1220                                 
1221                                 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
1222                                 break;
1223                         case 'rfc822GMT':
1224                                 /*
1225                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES
1226                                  *  5.  DATE AND TIME SPECIFICATION
1227                                  *   http://www.ietf.org/rfc/rfc0822.txt
1228                                  */
1229                                 $format = '%a, %d %m %y %H:%M:%S ';
1230                                 $timestamp -= $offset;
1231                                 $suffix = 'GMT';
1232                                 break;
1233                         case 'iso8601':
1234                         case 'rfc3339':
1235                                 /*
1236                                  * RFC3339: Date and Time on the Internet: Timestamps
1237                                  *  5. Date and Time format
1238                                  *   http://www.ietf.org/rfc/rfc3339.txt
1239                                  */
1240                                 $format = '%Y-%m-%dT%H:%M:%S';
1241                                 if ( $offset < 0 )
1242                                 {
1243                                         $suffix = '-';
1244                                         $offset = -$offset;
1245                                 }
1246                                 else
1247                                 {
1248                                         $suffix = '+';
1249                                 }
1250                                 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
1251                                 break;
1252                         case 'utc':
1253                         case 'iso8601UTC':
1254                         case 'rfc3339UTC':
1255                                 /*
1256                                  * RFC3339: Date and Time on the Internet: Timestamps
1257                                  *  5. Date and Time format
1258                                  *   http://www.ietf.org/rfc/rfc3339.txt
1259                                  */
1260                                 $timestamp -= $offset;
1261                                 $format = '%Y-%m-%dT%H:%M:%SZ';
1262                                 $suffix = '';
1263                                 break;
1264                         case '':
1265                                 $format = '%X %x';
1266                                 $offset = '';
1267                                 break;
1268                         default:
1269                                 $suffix = '';
1270                                 break;
1271                 }
1272                 return i18n::strftime($format, $timestamp) . $suffix;
1273         }
1274         
1275         /**
1276          * i18n::convert_locale_to_old_language_file_name()
1277          * NOTE: this should be obsoleted near future.
1278          * 
1279          * @static
1280          * @param       string  $target_locale  locale name as language_script_region
1281          * @return      string  old translation file name
1282          */
1283         static public function convert_locale_to_old_language_file_name($target_locale)
1284         {
1285                 $target_language = '';
1286                 foreach ( self::$lang_refs as $language => $locale )
1287                 {
1288                         if ( preg_match('#-#', $language) )
1289                         {
1290                                 if ( $target_locale . '.' . self::$current_charset == $locale )
1291                                 {
1292                                         $target_language = $language;
1293                                         break;
1294                                 }
1295                         }
1296                         else if ( $target_locale == $locale )
1297                         {
1298                                 $target_language = $language;
1299                         }
1300                 }
1301                 return $target_language;
1302         }
1303         
1304         /**
1305          * i18n::convert_old_language_file_name_to_locale()
1306          * NOTE: this should be obsoleted near future.
1307          * 
1308          * @static
1309          * @param       string  $target_language        old translation file name
1310          * @return      string  locale name as language_script_region
1311          */
1312         static public function convert_old_language_file_name_to_locale($target_language)
1313         {
1314                 $target_locale = '';
1315                 foreach ( self::$lang_refs as $language => $locale )
1316                 {
1317                         if ( $target_language == $language )
1318                         {
1319                                 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
1320                                 {
1321                                         $target_locale = $match[1];
1322                                 }
1323                                 else
1324                                 {
1325                                         $target_locale = $locale;
1326                                 }
1327                                 break;
1328                         }
1329                 }
1330                 return $target_locale;
1331         }
1332         
1333         /**
1334          * i18n::$lang_refs
1335          * reference table to convert old and new way to name translation files.
1336          * NOTE: this should be obsoleted as soon as possible.
1337          * 
1338          * @static
1339          */
1340         static private $lang_refs = array(
1341                 "english"               => "en_Latn_US",
1342                 "english-utf8"  => "en_Latn_US.UTF-8",
1343                 "bulgarian"     => "bg_Cyrl_BG",
1344                 "finnish"               => "fi_Latn_FI",
1345                 "catalan"               => "ca_Latn_ES",
1346                 "french"                => "fr_Latn_FR",
1347                 "russian"               => "ru_Cyrl_RU",
1348                 "chinese"               => "zh_Hans_CN",
1349                 "simchinese"    => "zh_Hans_CN",
1350                 "chineseb5"     => "zh_Hant_TW",
1351                 "traditional_chinese"   =>      "zh_Hant_TW",
1352                 "galego"                => "gl_Latn_ES",
1353                 "german"                => "de_Latn_DE",
1354                 "korean-utf"    => "ko_Kore_KR.UTF-8",
1355                 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
1356                 "slovak"                => "sk_Latn_SK",
1357                 "czech"         => "cs_Latn_CZ",
1358                 "hungarian"     => "hu_Latn_HU",
1359                 "latvian"               => "lv_Latn_LV",
1360                 "nederlands"    => "nl_Latn_NL",
1361                 "italiano"              => "it_Latn_IT",
1362                 "persian"               => "fa_Arab_IR",
1363                 "spanish"               => "es_Latn_ES",
1364                 "spanish-utf8"  => "es_Latn_ES.UTF-8",
1365                 "japanese-euc"  => "ja_Jpan_JP.EUC-JP",
1366                 "japanese-utf8" => "ja_Jpan_JP.UTF-8",
1367                 "portuguese_brazil"     => "pt_Latn_BR"
1368         );
1369 }
1370 >>>>>>> skinnable-master