OSDN Git Service

MERGE: リビジョン1746/1751のマージ。i18n::explode()の廃止とコメント修正。
[nucleus-jp/nucleus-next.git] / nucleus / libs / i18n.php
1 <?php\r
2 /**\r
3  * i18n class for Nucleus CMS\r
4  * written by Takashi Sakamoto as of Feb 03, 2012\r
5  * \r
6  * This includes wrapper functions of iconv and mbstring\r
7  * for multibyte processing and includes parameters related to locale.\r
8  * \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
14  *\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
18  */\r
19 class i18n\r
20 {\r
21         static private $mode = FALSE;\r
22         \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
29         \r
30         /**\r
31          * i18n::init\r
32          * Initializing i18n class\r
33          * \r
34          * @static\r
35          * @param       string  $charset        character set\r
36          * @return      boolean \r
37          */\r
38         static public function init($charset, $dir)\r
39         {\r
40                 /* i18n is already initialized */\r
41                 if ( self::$mode )\r
42                 {\r
43                         return TRUE;\r
44                 }\r
45                 \r
46                 /* make locale list in this Nucleus CMS */\r
47                 if ( ($handle = opendir($dir)) === FALSE )\r
48                 {\r
49                         return FALSE;\r
50                 }\r
51                 while ($filename = readdir($handle))\r
52                 {\r
53                         if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )\r
54                         {\r
55                                 if ( !in_array($matches[1], self::$locale_list) )\r
56                                 {\r
57                                         self::$locale_list[] = $matches[1];\r
58                                 }\r
59                         }\r
60                 }\r
61                 closedir($handle);\r
62                 \r
63                 /* set i18n backend and validate character set */\r
64                 if ( extension_loaded('iconv') )\r
65                 {\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
70                         {\r
71                                 self::$charset = $charset;\r
72                                 self::$mode = 'iconv';\r
73                         }\r
74                 }\r
75                 else if ( extension_loaded('mbstring') )\r
76                 {\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
81                         {\r
82                                 self::$charset = $charset;\r
83                                 self::$mode = 'mbstring';\r
84                         }\r
85                 }\r
86                 \r
87                 return TRUE;\r
88         }\r
89         \r
90         /**\r
91          * i18n::get_available_locale_list\r
92          * return available locale list with current charset\r
93          * \r
94          * @static\r
95          * @param       void\r
96          * @return      array   available locale list\r
97          */\r
98         static public function get_available_locale_list()\r
99         {\r
100                 return self::$locale_list;\r
101         }\r
102         \r
103         /**\r
104          * i18n::get_current_charset\r
105          * return current charset\r
106          * \r
107          * @static\r
108          * @param       void\r
109          * @return      string  $charset        current character set\r
110          */\r
111         static public function get_current_charset()\r
112         {\r
113                 return self::$charset;\r
114         }\r
115         \r
116         /**\r
117          * i18n::set_locale\r
118          * Set current locale\r
119          * \r
120          * NOTE:\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
124          * \r
125          * @static\r
126          * @param       string  $locale\r
127          * @return      bool    TRUE/FALSE\r
128          * \r
129          */\r
130         static public function set_current_locale($locale)\r
131         {\r
132                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )\r
133                 {\r
134                         self::$language = $match[1];\r
135                         self::$script   = $match[2];\r
136                         self::$region   = $match[3];\r
137                         return TRUE;\r
138                 }\r
139                 return FALSE;\r
140         }\r
141         \r
142         /**\r
143          * i18n::get_locale\r
144          * Get current locale\r
145          * \r
146          * @static\r
147          * @param       void\r
148          * @return      $locale\r
149          */\r
150         static public function get_current_locale()\r
151         {\r
152                 $elements = array(self::$language, self::$script, self::$region);\r
153                 return implode('_', $elements);\r
154         }\r
155         \r
156         /**\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
159          * \r
160          * NOTE:\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
163          * \r
164          * @static\r
165          * @param       void\r
166          * @return      void\r
167          */\r
168         static public function confirm_default_date_timezone()\r
169         {\r
170                 if ( function_exists('date_default_timezone_get') \r
171                  && FALSE !== ($timezone = @date_default_timezone_get()))\r
172                 {\r
173                         self::$timezone = $timezone;\r
174                 }\r
175                 if (function_exists('date_default_timezone_set')) {\r
176                          @date_default_timezone_set(self::$timezone);\r
177                 }\r
178                 return;\r
179         }\r
180         \r
181         /**\r
182          * i18n::get_current_date_timezone()\r
183          * get current timezone\r
184          * \r
185          * @static\r
186          * @param       void\r
187          * @return      $timezone\r
188          */\r
189         static public function get_date_timezone()\r
190         {\r
191                 return self::$timezone;\r
192         }\r
193         \r
194         /**\r
195          * i18n::convert\r
196          * character set converter\r
197          * \r
198          * @static\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
203          */\r
204         static public function convert($string, $from, $to='')\r
205         {\r
206                 if ( $to == '' )\r
207                 {\r
208                         $to = self::$charset;\r
209                 }\r
210                 \r
211                 if ( self::$mode == 'iconv' )\r
212                 {\r
213                         $string = iconv($from, $to.'//TRANSLIT', $string);\r
214                 }\r
215                 else if ( self::$mode == 'mbstring' )\r
216                 {\r
217                         $string = mb_convert_encoding($string, $to, $from);\r
218                 }\r
219                 return (string) $string;\r
220         }\r
221         \r
222         /**\r
223          * i18n::strlen\r
224          * strlen wrapper\r
225          * \r
226          * @static\r
227          * @param       string  $string target string\r
228          * @return      integer the number of letters\r
229          */\r
230         static public function strlen($string)\r
231         {\r
232                 $length = 0;\r
233                 if ( self::$mode == 'iconv' )\r
234                 {\r
235                         $length = iconv_strlen($string, self::$charset);\r
236                 }\r
237                 else if ( self::$mode == 'mbstring' )\r
238                 {\r
239                         $length = mb_strlen($string, self::$charset);\r
240                 }\r
241                 else\r
242                 {\r
243                         $length = strlen($string);\r
244                 }\r
245                 return (integer) $length;\r
246         }\r
247         \r
248         /**\r
249          * i18n::strpos\r
250          * strpos wrapper\r
251          * \r
252          * @static\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
257          */\r
258         static public function strpos($haystack, $needle, $offset=0)\r
259         {\r
260                 $position = 0;\r
261                 if ( self::$mode == 'iconv' )\r
262                 {\r
263                         $position = iconv_strpos($haystack, $needle, $offset, self::$charset);\r
264                 }\r
265                 else if ( self::$mode == 'mbstring' )\r
266                 {\r
267                         $position = mb_strpos($haystack, $needle, $offset, self::$charset);\r
268                 }\r
269                 else\r
270                 {\r
271                         $position = strpos($haystack, $needle, $offset);\r
272                 }\r
273                 \r
274                 if ( $position !== FALSE)\r
275                 {\r
276                         $position = (integer) $position;\r
277                 }\r
278                 return $position;\r
279         }\r
280         \r
281         /**\r
282          * i18n::strrpos\r
283          * strrpos wrapper\r
284          * \r
285          * @static\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
289          */\r
290         static public function strrpos ($haystack, $needle)\r
291         {\r
292                 $position = 0;\r
293                 if ( self::$mode == 'iconv' )\r
294                 {\r
295                         $position = iconv_strrpos($haystack, $needle, self::$charset);\r
296                 }\r
297                 else if ( self::$mode == 'mbstring' )\r
298                 {\r
299                         $position = mb_strrpos($haystack, $needle, 0, self::$charset);\r
300                 }\r
301                 else\r
302                 {\r
303                         $position = strrpos($haystack, $needle, 0);\r
304                 }\r
305                 \r
306                 if ( $position !== FALSE)\r
307                 {\r
308                         $position = (integer) $position;\r
309                 }\r
310                 return $position;\r
311         }\r
312         \r
313         /**\r
314          * i18n::substr\r
315          * substr wrapper\r
316          * \r
317          * @static\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
322          */\r
323         static public function substr($string, $start, $length=0)\r
324         {\r
325                 $return = '';\r
326                 if ( self::$mode == 'iconv' )\r
327                 {\r
328                         $return = iconv_substr($string, $start, $length, self::$charset);\r
329                 }\r
330                 else if ( self::$mode == 'mbstring' )\r
331                 {\r
332                         $return = mb_substr($string, $start, $length, self::$charset);\r
333                 }\r
334                 else\r
335                 {\r
336                         $return = strrpos($string, $start, $length);\r
337                 }\r
338                 return (string) $return;\r
339         }\r
340         \r
341         /**\r
342          * i18n::strftime\r
343          * strftime function based on multibyte processing\r
344          * \r
345          * @static\r
346          * @param       string  $format format with singlebyte or multibyte\r
347          * @param       timestamp       $timestamp      UNIX timestamp\r
348          * @return      string  formatted timestamp\r
349          */\r
350         static public function strftime($format, $timestamp='')\r
351         {\r
352                 $formatted = '';\r
353                 \r
354                 if ( $timestamp == '' )\r
355                 {\r
356                         $timestamp = time();\r
357                 }\r
358                 \r
359                 if ( $format == '%%' )\r
360                 {\r
361                         return '%';\r
362                 }\r
363                 else if ( preg_match('#%[^%]#', $format) === 0 )\r
364                 {\r
365                         return $format;\r
366                 }\r
367                 \r
368                 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');\r
369                 $elements = preg_split('#,#', $format);\r
370                 \r
371                 foreach ( $elements as $element )\r
372                 {\r
373                         if ( preg_match('#(%[^%])#', $element) )\r
374                         {\r
375                                 $formatted .= strftime($element, $timestamp);\r
376                         }\r
377                         else if ( $element == '%%' )\r
378                         {\r
379                                 $formatted .= '%';\r
380                         }\r
381                         else\r
382                         {\r
383                                 $formatted .= $element;\r
384                         }\r
385                 }\r
386                 \r
387                 return (string) $formatted;\r
388         }\r
389         \r
390         /**\r
391          * i18n::formatted_datetime()\r
392          * return formatted datetime string\r
393          * \r
394          * Date and Time Formats\r
395          * @link        http://www.w3.org/TR/NOTE-datetime\r
396          * \r
397          * Working with Time Zones\r
398          * @link        http://www.w3.org/TR/timezone/\r
399          * \r
400          * @param       String  $format time expression format\r
401          * @param       String  $timestamp      UNIX timestamp\r
402          * @param       Integer $offset timestamp offset\r
403          * @return      String  formatted datetime\r
404          */\r
405         static public function formatted_datetime($format, $timestamp, $offset=0)\r
406         {\r
407                 $suffix = '';\r
408                 $string = '';\r
409                 \r
410                 switch ( $format )\r
411                 {\r
412                         case 'mysql':\r
413                                 /*\r
414                                  * MySQL 5.0 Reference Manual\r
415                                  *  10.3.1. The DATE, DATETIME, and TIMESTAMP Types\r
416                                  *   http://dev.mysql.com/doc/refman/5.0/en/datetime.html\r
417                                  */\r
418                                 $timestamp += $offset;\r
419                                 $format = '%Y-%m-%d %H:%M:%S';\r
420                                 $suffix ='';\r
421                                 break;\r
422                         case 'rfc822':\r
423                                 /*\r
424                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
425                                  *  5.  DATE AND TIME SPECIFICATION\r
426                                  *   http://www.ietf.org/rfc/rfc0822.txt\r
427                                  */\r
428                                 $format = '%a, %d %m %y %H:%M:%S ';\r
429                                 if ( $offset < 0 )\r
430                                 {\r
431                                         $suffix = '-';\r
432                                         $offset = -$offset;\r
433                                 }\r
434                                 else\r
435                                 {\r
436                                         $suffix = '+';\r
437                                 }\r
438                                 \r
439                                 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
440                                 break;\r
441                         case 'rfc822GMT':\r
442                                 /*\r
443                                  * RFC 822: STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES\r
444                                  *  5.  DATE AND TIME SPECIFICATION\r
445                                  *   http://www.ietf.org/rfc/rfc0822.txt\r
446                                  */\r
447                                 $format = '%a, %d %m %y %H:%M:%S ';\r
448                                 $timestamp -= $offset;\r
449                                 $suffix = 'GMT';\r
450                                 break;\r
451                         case 'iso8601':\r
452                         case 'rfc3339':\r
453                                 /*\r
454                                  * RFC3339: Date and Time on the Internet: Timestamps\r
455                                  *  5. Date and Time format\r
456                                  *   http://www.ietf.org/rfc/rfc3339.txt\r
457                                  */\r
458                                 $format = '%Y-%m-%dT%H:%M:%S';\r
459                                 if ( $offset < 0 )\r
460                                 {\r
461                                         $suffix = '-';\r
462                                         $offset = -$offset;\r
463                                 }\r
464                                 else\r
465                                 {\r
466                                         $suffix = '+';\r
467                                 }\r
468                                 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );\r
469                                 break;\r
470                         case 'utc':\r
471                         case 'iso8601UTC':\r
472                         case 'rfc3339UTC':\r
473                                 /*\r
474                                  * RFC3339: Date and Time on the Internet: Timestamps\r
475                                  *  5. Date and Time format\r
476                                  *   http://www.ietf.org/rfc/rfc3339.txt\r
477                                  */\r
478                                 $timestamp -= $offset;\r
479                                 $format = '%Y-%m-%dT%H:%M:%SZ';\r
480                                 $suffix = '';\r
481                                 break;\r
482                         case '':\r
483                                 $format = '%X %x';\r
484                                 $offset = '';\r
485                                 break;\r
486                         default:\r
487                                 $suffix = '';\r
488                                 break;\r
489                 }\r
490                 return i18n::strftime($format, $timestamp) . $suffix;\r
491         }\r
492         \r
493         /**\r
494          * i18n::convert_locale_to_old_language_file_name()\r
495          * NOTE: this should be obsoleted near future.\r
496          * \r
497          * @static\r
498          * @param       string  $target_locale  locale name as language_script_region\r
499          * @return      string  old translation file name\r
500          */\r
501         static public function convert_locale_to_old_language_file_name($target_locale)\r
502         {\r
503                 $target_language = '';\r
504                 foreach ( self::$lang_refs as $language => $locale )\r
505                 {\r
506                         if ( preg_match('#-#', $language) )\r
507                         {\r
508                                 if ( $target_locale . '.' . self::$charset == $locale )\r
509                                 {\r
510                                         $target_language = $language;\r
511                                         break;\r
512                                 }\r
513                         }\r
514                         else if ( $target_locale == $locale )\r
515                         {\r
516                                 $target_language = $language;\r
517                         }\r
518                 }\r
519                 return $target_language;\r
520         }\r
521         \r
522         /**\r
523          * i18n::convert_old_language_file_name_to_locale()\r
524          * NOTE: this should be obsoleted near future.\r
525          * \r
526          * @static\r
527          * @param       string  $target_language        old translation file name\r
528          * @return      string  locale name as language_script_region\r
529          */\r
530         static public function convert_old_language_file_name_to_locale($target_language)\r
531         {\r
532                 $target_locale = '';\r
533                 foreach ( self::$lang_refs as $language => $locale )\r
534                 {\r
535                         if ( $target_language == $language )\r
536                         {\r
537                                 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )\r
538                                 {\r
539                                         $target_locale = $match[1];\r
540                                 }\r
541                                 else\r
542                                 {\r
543                                         $target_locale = $locale;\r
544                                 }\r
545                                 break;\r
546                         }\r
547                 }\r
548                 return $target_locale;\r
549         }\r
550         \r
551         /**\r
552          * i18n::$lang_refs\r
553          * reference table to convert old and new way to name translation files.\r
554          * NOTE: this should be obsoleted as soon as possible.\r
555          * \r
556          * @static\r
557          */\r
558         static private $lang_refs = array(\r
559                 "english"               => "en_Latn_US",\r
560                 "english-utf8"  => "en_Latn_US.UTF-8",\r
561                 "bulgarian"     => "bg_Cyrl_BG",\r
562                 "finnish"               => "fi_Latn_FU",\r
563                 "catalan"               => "ca_Latn_ES",\r
564                 "french"                => "fr_Latn_FR",\r
565                 "russian"               => "ru_Cyrl_RU",\r
566                 "chinese"               => "zh_Hans_CN",\r
567                 "simchinese"    => "zh_Hans_CN",\r
568                 "chineseb5"     => "zh_Hant_TW",\r
569                 "traditional_chinese"   =>      "zh_Hant_TW",\r
570                 "galego"                => "gl_Latn_ES",\r
571                 "german"                => "de_Latn_DE",\r
572                 "korean-utf"    => "ko_Kore_KR.UTF-8",\r
573                 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",\r
574                 "slovak"                => "sk_Latn_SK",\r
575                 "czech"         => "cs_Latn_CZ",\r
576                 "hungarian"     => "hu_Latn_HU",\r
577                 "latvian"               => "lv_Latn_LV",\r
578                 "nederlands"    => "nl_Latn_NL",\r
579                 "italiano"              => "it_Latn_IT",\r
580                 "persian"               => "fa_Arab_IR",\r
581                 "spanish"               => "es_Latn_ES",\r
582                 "spanish-utf8"  => "es_Latn_ES.UTF-8",\r
583                 "japanese-euc"  => "ja_Jpan_JP.EUC-JP",\r
584                 "japanese-utf8" => "ja_Jpan_JP.UTF-8",\r
585                 "portuguese_brazil"     => "pt_Latn_BR"\r
586         );\r
587 }\r