OSDN Git Service

CHANGE: フィードとゲストアカウント作成フォームのためのスクリプトを修正
[nucleus-jp/nucleus-next.git] / nucleus / libs / i18n.php
1 <?php
2 /**
3  * i18n class for Nucleus CMS
4  * written by Takashi Sakamoto as of Feb 03, 2012
5  * 
6  * This includes wrapper functions of iconv and mbstring
7  * for multibyte processing and includes parameters related to locale.
8  * 
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)
14  *
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 $
18  */
19 class i18n
20 {
21         static private $mode = FALSE;
22         
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';
29         
30         /**
31          * i18n::init
32          * Initializing i18n class
33          * 
34          * @static
35          * @param       string  $charset        character set
36          * @return      boolean 
37          */
38         static public function init($charset, $dir)
39         {
40                 /* i18n is already initialized */
41                 if ( self::$mode )
42                 {
43                         return TRUE;
44                 }
45                 
46                 /* make locale list in this Nucleus CMS */
47                 if ( ($handle = opendir($dir)) === FALSE )
48                 {
49                         return FALSE;
50                 }
51                 while ($filename = readdir($handle))
52                 {
53                         if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
54                         {
55                                 if ( !in_array($matches[1], self::$locale_list) )
56                                 {
57                                         self::$locale_list[] = $matches[1];
58                                 }
59                         }
60                 }
61                 closedir($handle);
62                 
63                 /* set i18n backend and validate character set */
64                 if ( extension_loaded('iconv') )
65                 {
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) )
70                         {
71                                 self::$charset = $charset;
72                                 self::$mode = 'iconv';
73                         }
74                 }
75                 else if ( extension_loaded('mbstring') )
76                 {
77                         /* this is just for checking the charset. */
78                         if ( mb_http_output($charset)
79                          && mb_internal_encoding($charset)
80                          && mb_regex_encoding($charset) )
81                         {
82                                 self::$charset = $charset;
83                                 self::$mode = 'mbstring';
84                         }
85                 }
86                 
87                 return TRUE;
88         }
89         
90         /**
91          * i18n::get_available_locale_list
92          * return available locale list with current charset
93          * 
94          * @static
95          * @param       void
96          * @return      array   available locale list
97          */
98         static public function get_available_locale_list()
99         {
100                 return self::$locale_list;
101         }
102         
103         /**
104          * i18n::get_current_charset
105          * return current charset
106          * 
107          * @static
108          * @param       void
109          * @return      string  $charset        current character set
110          */
111         static public function get_current_charset()
112         {
113                 return self::$charset;
114         }
115         
116         /**
117          * i18n::set_locale
118          * Set current locale
119          * 
120          * NOTE:
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
124          * 
125          * @static
126          * @param       string  $locale
127          * @return      bool    TRUE/FALSE
128          * 
129          */
130         static public function set_current_locale($locale)
131         {
132                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
133                 {
134                         self::$language = $match[1];
135                         self::$script   = $match[2];
136                         self::$region   = $match[3];
137                         return TRUE;
138                 }
139                 return FALSE;
140         }
141         
142         /**
143          * i18n::get_locale
144          * Get current locale
145          * 
146          * @static
147          * @param       void
148          * @return      $locale
149          */
150         static public function get_current_locale()
151         {
152                 $elements = array(self::$language, self::$script, self::$region);
153                 return implode('_', $elements);
154         }
155         
156         /**
157          * i18n::confirm_default_date_timezone
158          * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
159          * 
160          * NOTE:
161          * Some private servers are lack of its timezone setting
162          * http://www.php.net/manual/en/function.date-default-timezone-set.php
163          * 
164          * @static
165          * @param       void
166          * @return      void
167          */
168         static public function confirm_default_date_timezone()
169         {
170                 if ( function_exists('date_default_timezone_get') 
171                  && FALSE !== ($timezone = @date_default_timezone_get()))
172                 {
173                         self::$timezone = $timezone;
174                 }
175                 if (function_exists('date_default_timezone_set')) {
176                          @date_default_timezone_set(self::$timezone);
177                 }
178                 return;
179         }
180         
181         /**
182          * i18n::get_current_date_timezone()
183          * get current timezone
184          * 
185          * @static
186          * @param       void
187          * @return      $timezone
188          */
189         static public function get_date_timezone()
190         {
191                 return self::$timezone;
192         }
193         
194         /**
195          * i18n::convert
196          * character set converter
197          * 
198          * @static
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
203          */
204         static public function convert($string, $from, $to='')
205         {
206                 if ( $to == '' )
207                 {
208                         $to = self::$charset;
209                 }
210                 
211                 if ( self::$mode == 'iconv' )
212                 {
213                         $string = iconv($from, $to.'//TRANSLIT', $string);
214                 }
215                 else if ( self::$mode == 'mbstring' )
216                 {
217                         $string = mb_convert_encoding($string, $to, $from);
218                 }
219                 return (string) $string;
220         }
221         
222         /**
223          * i18n::strlen
224          * strlen wrapper
225          * 
226          * @static
227          * @param       string  $string target string
228          * @return      integer the number of letters
229          */
230         static public function strlen($string)
231         {
232                 $length = 0;
233                 if ( self::$mode == 'iconv' )
234                 {
235                         $length = iconv_strlen($string, self::$charset);
236                 }
237                 else if ( self::$mode == 'mbstring' )
238                 {
239                         $length = mb_strlen($string, self::$charset);
240                 }
241                 else
242                 {
243                         $length = strlen($string);
244                 }
245                 return (integer) $length;
246         }
247         
248         /**
249          * i18n::strpos
250          * strpos wrapper
251          * 
252          * @static
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
257          */
258         static public function strpos($haystack, $needle, $offset=0)
259         {
260                 $position = 0;
261                 if ( self::$mode == 'iconv' )
262                 {
263                         $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
264                 }
265                 else if ( self::$mode == 'mbstring' )
266                 {
267                         $position = mb_strpos($haystack, $needle, $offset, self::$charset);
268                 }
269                 else
270                 {
271                         $position = strpos($haystack, $needle, $offset);
272                 }
273                 
274                 if ( $position !== FALSE)
275                 {
276                         $position = (integer) $position;
277                 }
278                 return $position;
279         }
280         
281         /**
282          * i18n::strrpos
283          * strrpos wrapper
284          * 
285          * @static
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
289          */
290         static public function strrpos ($haystack, $needle)
291         {
292                 $position = 0;
293                 if ( self::$mode == 'iconv' )
294                 {
295                         $position = iconv_strrpos($haystack, $needle, self::$charset);
296                 }
297                 else if ( self::$mode == 'mbstring' )
298                 {
299                         $position = mb_strrpos($haystack, $needle, 0, self::$charset);
300                 }
301                 else
302                 {
303                         $position = strrpos($haystack, $needle, 0);
304                 }
305                 
306                 if ( $position !== FALSE)
307                 {
308                         $position = (integer) $position;
309                 }
310                 return $position;
311         }
312         
313         /**
314          * i18n::substr
315          * substr wrapper
316          * 
317          * @static
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
322          */
323         static public function substr($string, $start, $length=0)
324         {
325                 $return = '';
326                 if ( self::$mode == 'iconv' )
327                 {
328                         $return = iconv_substr($string, $start, $length, self::$charset);
329                 }
330                 else if ( self::$mode == 'mbstring' )
331                 {
332                         $return = mb_substr($string, $start, $length, self::$charset);
333                 }
334                 else
335                 {
336                         $return = strrpos($string, $start, $length);
337                 }
338                 return (string) $return;
339         }
340         
341         /**
342          * i18n::explode()
343          * explode function based on multibyte processing with non-pcre regular expressions
344          * 
345          * NOTE: we SHOULD use preg_split function instead of this,
346          *  and I hope this is obsoleted near future...
347          *  
348          *  preg_split()
349          *  http://www.php.net/manual/en/function.preg-split.php
350          * 
351          * @static
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
356          */
357         static public function explode($delimiter, $target, $limit=0)
358         {
359                 $array = array();
360                 $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
361                 if ( preg_match($preg_delimiter, $target) === 0 )
362                 {
363                         return (array) $target;
364                 }
365                 for ( $count=0; $limit == 0 || $count < $limit; $count++ )
366                 {
367                         $offset = self::strpos($target, $delimiter);
368                         if ( $array != array() && $offset == 0 )
369                         {
370                                 $array[] = $target;
371                                 break;
372                         }
373                         $array[]        = self::substr($target, 0, $offset);
374                         $length = self::strlen($target) - $offset;
375                         $target = self::substr($target, $offset+1, $length);
376                         continue;
377                 }
378                 return (array) $array;
379         }
380         
381         /**
382          * i18n::strftime
383          * strftime function based on multibyte processing
384          * 
385          * @static
386          * @param       string  $format format with singlebyte or multibyte
387          * @param       timestamp       $timestamp      UNIX timestamp
388          * @return      string  formatted timestamp
389          */
390         static public function strftime($format, $timestamp='')
391         {
392                 $formatted = '';
393                 
394                 if ( $timestamp == '' )
395                 {
396                         $timestamp = time();
397                 }
398                 
399                 if ( $format == '%%' )
400                 {
401                         return '%';
402                 }
403                 else if ( preg_match('#%[^%]#', $format) === 0 )
404                 {
405                         return $format;
406                 }
407                 
408                 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
409                 $elements = preg_split('#,#', $format);
410                 
411                 foreach ( $elements as $element )
412                 {
413                         if ( preg_match('#(%[^%])#', $element) )
414                         {
415                                 $formatted .= strftime($element, $timestamp);
416                         }
417                         else if ( $element == '%%' )
418                         {
419                                 $formatted .= '%';
420                         }
421                         else
422                         {
423                                 $formatted .= $element;
424                         }
425                 }
426                 
427                 return (string) $formatted;
428         }
429         
430         /**
431          * i18n::formatted_datetime()
432          * return formatted datetime string
433          * 
434          * Date and Time Formats
435          * @link        http://www.w3.org/TR/NOTE-datetime
436          * 
437          * Working with Time Zones
438          * @link        http://www.w3.org/TR/timezone/
439          * 
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
445          */
446         static public function formatted_datetime($format, $timestamp, $default_format, $offset)
447         {
448                 $suffix = '';
449                 $string = '';
450                 
451                 switch ( $format )
452                 {
453                         case 'rfc822':
454                                 $format = 'D, j M Y H:i:s ';
455                                 if ( $offset < 0 )
456                                 {
457                                         $suffix = '-';
458                                         $offset = -$offset;
459                                 }
460                                 else
461                                 {
462                                         $suffix = '+';
463                                 }
464                                 
465                                 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
466                                 break;
467                         case 'rfc822GMT':
468                                 $format = 'D, j M Y H:i:s ';
469                                 $timestamp -= $offset;
470                                 $suffix = 'GMT';
471                                 break;
472                         case 'utc':
473                                 $timestamp -= $offset;
474                                 $format = 'Y-m-d\TH:i:s\Z';
475                                 $suffix = '';
476                                 break;
477                         case 'iso8601':
478                                 $format = 'Y-m-d\TH:i:s';
479                                 if ( $offset < 0 )
480                                 {
481                                         $suffix = '-';
482                                         $offset = -$offset;
483                                 }
484                                 else
485                                 {
486                                         $suffix = '+';
487                                 }
488                                 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
489                                 break;
490                         case '':
491                                 $format = $default_format;
492                                 $suffix = '';
493                                 break;
494                         default:
495                                 $suffix = 0;
496                                 break;
497                 }
498                 return i18n::strftime($format, $timestamp) . $suffix;
499         }
500         
501         /**
502          * i18n::convert_locale_to_old_language_file_name()
503          * NOTE: this should be obsoleted near future.
504          * 
505          * @static
506          * @param       string  $target_locale  locale name as language_script_region
507          * @return      string  old translation file name
508          */
509         static public function convert_locale_to_old_language_file_name($target_locale)
510         {
511                 $target_language = '';
512                 foreach ( self::$lang_refs as $language => $locale )
513                 {
514                         if ( preg_match('#-#', $language) )
515                         {
516                                 if ( $target_locale . '.' . self::$charset == $locale )
517                                 {
518                                         $target_language = $language;
519                                         break;
520                                 }
521                         }
522                         else if ( $target_locale == $locale )
523                         {
524                                 $target_language = $language;
525                         }
526                 }
527                 return $target_language;
528         }
529         
530         /**
531          * i18n::convert_old_language_file_name_to_locale()
532          * NOTE: this should be obsoleted near future.
533          * 
534          * @static
535          * @param       string  $target_language        old translation file name
536          * @return      string  locale name as language_script_region
537          */
538         static public function convert_old_language_file_name_to_locale($target_language)
539         {
540                 $target_locale = '';
541                 foreach ( self::$lang_refs as $language => $locale )
542                 {
543                         if ( $target_language == $language )
544                         {
545                                 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
546                                 {
547                                         $target_locale = $match[1];
548                                 }
549                                 else
550                                 {
551                                         $target_locale = $locale;
552                                 }
553                                 break;
554                         }
555                 }
556                 return $target_locale;
557         }
558         
559         /**
560          * i18n::$lang_refs
561          * reference table to convert old and new way to name translation files.
562          * NOTE: this should be obsoleted as soon as possible.
563          * 
564          * @static
565          */
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"
594         );
595 }