OSDN Git Service

CHANGE: 言語(language)となっていたディレクトリ・定数・変数・翻訳・ドキュメントをロケール(locale)に修正。
[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  * and mail function with 7bit characters encoder
8  * for multibyte processing and includes members related to locale.
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * (see nucleus/documentation/index.html#license for more info)
15  */
16 class i18n
17 {
18         static private $mode = FALSE;
19         
20         static private $charset = '';
21         static private $language = '';
22         static private $script = '';
23         static private $region = '';
24         static private $locale_list = array();
25         static private $timezone = 'UTC';
26         
27         /**
28          * i18n::init
29          * Initializing i18n class
30          * 
31          * @static
32          * @param       string  $charset        character set
33          * @return      boolean 
34          */
35         static public function init($charset, $dir)
36         {
37                 /* i18n is already initialized */
38                 if ( self::$mode )
39                 {
40                         return TRUE;
41                 }
42                 
43                 /* make locale list in this Nucleus CMS */
44                 if ( ($handle = opendir($dir)) === FALSE )
45                 {
46                         return FALSE;
47                 }
48                 while ($filename = readdir($handle))
49                 {
50                         if (preg_match("#^(.+_.+_.+)\.{$charset}\.php$#", $filename, $matches) )
51                         {
52                                 if ( !in_array($matches[1], self::$locale_list) )
53                                 {
54                                         self::$locale_list[] = $matches[1];
55                                 }
56                         }
57                 }
58                 closedir($handle);
59                 
60                 /* set i18n backend and validate character set */
61                 if ( extension_loaded('iconv') )
62                 {
63                         /* this is just for checking the charset. */
64                         if ( iconv_set_encoding('internal_encoding', $charset)
65                          && iconv_set_encoding('output_encoding', $charset)
66                          && iconv_set_encoding('internal_encoding', $charset) )
67                         {
68                                 self::$charset = $charset;
69                                 self::$mode = 'iconv';
70                         }
71                 }
72                 else if ( extension_loaded('mbstring') )
73                 {
74                         /* this is just for checking the charset. */
75                         if ( mb_http_output($charset)
76                          && mb_internal_encoding($charset)
77                          && mb_regex_encoding($charset) )
78                         {
79                                 self::$charset = $charset;
80                                 self::$mode = 'mbstring';
81                         }
82                 }
83                 
84                 return TRUE;
85         }
86         
87         /**
88          * i18n::get_available_locale_list
89          * return available locale list with current charset
90          * 
91          * @static
92          * @param       void
93          * @return      array   available locale list
94          */
95         static public function get_available_locale_list()
96         {
97                 return self::$locale_list;
98         }
99         
100         /**
101          * i18n::get_current_charset
102          * return current charset
103          * 
104          * @static
105          * @param       void
106          * @return      string  $charset        current character set
107          */
108         static public function get_current_charset()
109         {
110                 return self::$charset;
111         }
112         
113         /**
114          * i18n::set_locale
115          * Set current locale
116          * 
117          * NOTE:
118          * naming rule is "$language_$script_$region.$charset.php", refer to RFC 5646.
119          * @link http://www.ietf.org/rfc/rfc5646.txt
120          * @see 2.  The Language Tag
121          * 
122          * @static
123          * @param       string  $locale
124          * @return      bool    TRUE/FALSE
125          * 
126          */
127         static public function set_current_locale($locale)
128         {
129                 if ( preg_match('#^(.+)_(.+)_(.+)$#', $locale, $match) )
130                 {
131                         self::$language = $match[1];
132                         self::$script   = $match[2];
133                         self::$region   = $match[3];
134                         return TRUE;
135                 }
136                 return FALSE;
137         }
138         
139         /**
140          * i18n::get_locale
141          * Get current locale
142          * 
143          * @static
144          * @param       void
145          * @return      $locale
146          */
147         static public function get_current_locale()
148         {
149                 $elements = array(self::$language, self::$script, self::$region);
150                 return implode('_', $elements);
151         }
152         
153         /**
154          * i18n::confirm_default_date_timezone
155          * to avoid E_NOTICE or E_WARNING generated when every calling to a date/time function.
156          * 
157          * NOTE:
158          * Some private servers are lack of its timezone setting
159          * http://www.php.net/manual/en/function.date-default-timezone-set.php
160          * 
161          * @static
162          * @param       void
163          * @return      void
164          */
165         static public function confirm_default_date_timezone()
166         {
167                 if ( function_exists('date_default_timezone_get') 
168                  && FALSE !== ($timezone = @date_default_timezone_get()))
169                 {
170                         self::$timezone = $timezone;
171                 }
172                 if (function_exists('date_default_timezone_set')) {
173                          @date_default_timezone_set(self::$timezone);
174                 }
175                 return;
176         }
177         
178         /**
179          * i18n::get_current_date_timezone()
180          * get current timezone
181          * 
182          * @static
183          * @param       void
184          * @return      $timezone
185          */
186         static public function get_date_timezone()
187         {
188                 return self::$timezone;
189         }
190         
191         /**
192          * i18n::convert
193          * character set converter
194          * 
195          * @static
196          * @param       string  $string target string binary
197          * @param       string  $from   original character set encoding
198          * @param       string  $to     target character set encoding
199          * @return      string  converted string
200          */
201         static public function convert($string, $from, $to='')
202         {
203                 if ( $to == '' )
204                 {
205                         $to = self::$charset;
206                 }
207                 
208                 if ( self::$mode == 'iconv' )
209                 {
210                         $string = iconv($from, $to.'//TRANSLIT', $string);
211                 }
212                 else if ( self::$mode == 'mbstring' )
213                 {
214                         $string = mb_convert_encoding($string, $to, $from);
215                 }
216                 return (string) $string;
217         }
218         
219         /**
220          * i18n::strlen
221          * strlen wrapper
222          * 
223          * @static
224          * @param       string  $string target string
225          * @return      integer the number of letters
226          */
227         static public function strlen($string)
228         {
229                 $length = 0;
230                 if ( self::$mode == 'iconv' )
231                 {
232                         $length = iconv_strlen($string, self::$charset);
233                 }
234                 else if ( self::$mode == 'mbstring' )
235                 {
236                         $length = mb_strlen($string, self::$charset);
237                 }
238                 else
239                 {
240                         $length = strlen($string);
241                 }
242                 return (integer) $length;
243         }
244         
245         /**
246          * i18n::strpos
247          * strpos wrapper
248          * 
249          * @static
250          * @param       string  $haystack       string to search
251          * @param       string  $needle string for search
252          * @param       string  $offset the position from which the search should be performed. 
253          * @return      integer/FALSE   the numeric position of the first occurrence of needle in haystack
254          */
255         static public function strpos($haystack, $needle, $offset=0)
256         {
257                 $position = 0;
258                 if ( self::$mode == 'iconv' )
259                 {
260                         $position = iconv_strpos($haystack, $needle, $offset, self::$charset);
261                 }
262                 else if ( self::$mode == 'mbstring' )
263                 {
264                         $position = mb_strpos($haystack, $needle, $offset, self::$charset);
265                 }
266                 else
267                 {
268                         $position = strpos($haystack, $needle, $offset);
269                 }
270                 
271                 if ( $position !== FALSE)
272                 {
273                         $position = (integer) $position;
274                 }
275                 return $position;
276         }
277         
278         /**
279          * i18n::strrpos
280          * strrpos wrapper
281          * 
282          * @static
283          * @param       string  $haystack       string to search
284          * @param       string  $needle string for search
285          * @return      integer/FALSE   the numeric position of the last occurrence of needle in haystack
286          */
287         static public function strrpos ($haystack, $needle)
288         {
289                 $position = 0;
290                 if ( self::$mode == 'iconv' )
291                 {
292                         $position = iconv_strrpos($haystack, $needle, self::$charset);
293                 }
294                 else if ( self::$mode == 'mbstring' )
295                 {
296                         $position = mb_strrpos($haystack, $needle, 0, self::$charset);
297                 }
298                 else
299                 {
300                         $position = strrpos($haystack, $needle, 0);
301                 }
302                 
303                 if ( $position !== FALSE)
304                 {
305                         $position = (integer) $position;
306                 }
307                 return $position;
308         }
309         
310         /**
311          * i18n::substr
312          * substr wrapper
313          * 
314          * @static
315          * @param       string  $string string to be cut
316          * @param       string  $start  the position of starting
317          * @param       integer $length the length to be cut
318          * @return      string  the extracted part of string
319          */
320         static public function substr($string, $start, $length=0)
321         {
322                 $return = '';
323                 if ( self::$mode == 'iconv' )
324                 {
325                         $return = iconv_substr($string, $start, $length, self::$charset);
326                 }
327                 else if ( self::$mode == 'mbstring' )
328                 {
329                         $return = mb_substr($string, $start, $length, self::$charset);
330                 }
331                 else
332                 {
333                         $return = strrpos($string, $start, $length);
334                 }
335                 return (string) $return;
336         }
337         
338         /**
339          * i18n::explode()
340          * explode function based on multibyte processing with non-pcre regular expressions
341          * 
342          * NOTE: we SHOULD use preg_split function instead of this,
343          *  and I hope this is obsoleted near future...
344          *  
345          *  preg_split()
346          *  http://www.php.net/manual/en/function.preg-split.php
347          * 
348          * @static
349          * @param       string  $delimiter      singlebyte or multibyte delimiter
350          * @param       string  $target target string
351          * @param       integer $limit  the number of index for returned array
352          * @return      array   array splitted by $delimiter
353          */
354         static public function explode($delimiter, $target, $limit=0)
355         {
356                 $array = array();
357                 $preg_delimiter = '#' . preg_quote($delimiter, '#') . '#';
358                 if ( preg_match($preg_delimiter, $target) === 0 )
359                 {
360                         return (array) $target;
361                 }
362                 for ( $count=0; $limit == 0 || $count < $limit; $count++ )
363                 {
364                         $offset = self::strpos($target, $delimiter);
365                         if ( $array != array() && $offset == 0 )
366                         {
367                                 $array[] = $target;
368                                 break;
369                         }
370                         $array[]        = self::substr($target, 0, $offset);
371                         $length = self::strlen($target) - $offset;
372                         $target = self::substr($target, $offset+1, $length);
373                         continue;
374                 }
375                 return (array) $array;
376         }
377         
378         /**
379          * i18n::strftime
380          * strftime function based on multibyte processing
381          * 
382          * @static
383          * @param       string  $format format with singlebyte or multibyte
384          * @param       timestamp       $timestamp      UNIX timestamp
385          * @return      string  formatted timestamp
386          */
387         static public function strftime($format, $timestamp='')
388         {
389                 $formatted = '';
390                 
391                 if ( $timestamp == '' )
392                 {
393                         $timestamp = time();
394                 }
395                 
396                 if ( $format == '%%' )
397                 {
398                         return '%';
399                 }
400                 else if ( preg_match('#%[^%]#', $format) === 0 )
401                 {
402                         return $format;
403                 }
404                 
405                 $format = trim(preg_replace('#(%[^%])#', ',$1,', $format), ',');
406                 $elements = preg_split('#,#', $format);
407                 
408                 foreach ( $elements as $element )
409                 {
410                         if ( preg_match('#(%[^%])#', $element) )
411                         {
412                                 $formatted .= strftime($element, $timestamp);
413                         }
414                         else if ( $element == '%%' )
415                         {
416                                 $formatted .= '%';
417                         }
418                         else
419                         {
420                                 $formatted .= $element;
421                         }
422                 }
423                 
424                 return (string) $formatted;
425         }
426         
427         /**
428          * i18n::formatted_datetime()
429          * return formatted datetime string
430          * 
431          * Date and Time Formats
432          * @link        http://www.w3.org/TR/NOTE-datetime
433          * 
434          * Working with Time Zones
435          * @link        http://www.w3.org/TR/timezone/
436          * 
437          * @param       String  $format timezone format
438          * @param       String  $timestamp      UNIX timestamp
439          * @param       String  $default_format default timezone format
440          * @param       Integer $offset timestamp offset
441          * @return      String  formatted datetime
442          */
443         static public function formatted_datetime($format, $timestamp, $default_format, $offset)
444         {
445                 $suffix = '';
446                 $string = '';
447                 
448                 switch ( $format )
449                 {
450                         case 'rfc822':
451                                 $format = 'D, j M Y H:i:s ';
452                                 if ( $offset < 0 )
453                                 {
454                                         $suffix = '-';
455                                         $offset = -$offset;
456                                 }
457                                 else
458                                 {
459                                         $suffix = '+';
460                                 }
461                                 
462                                 $suffix .= sprintf("%02d%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
463                                 break;
464                         case 'rfc822GMT':
465                                 $format = 'D, j M Y H:i:s ';
466                                 $timestamp -= $offset;
467                                 $suffix = 'GMT';
468                                 break;
469                         case 'utc':
470                                 $timestamp -= $offset;
471                                 $format = 'Y-m-d\TH:i:s\Z';
472                                 $suffix = '';
473                                 break;
474                         case 'iso8601':
475                                 $format = 'Y-m-d\TH:i:s';
476                                 if ( $offset < 0 )
477                                 {
478                                         $suffix = '-';
479                                         $offset = -$offset;
480                                 }
481                                 else
482                                 {
483                                         $suffix = '+';
484                                 }
485                                 $suffix .= sprintf("%02d:%02d", floor($offset / 3600), round(($offset % 3600) / 60) );
486                                 break;
487                         case '':
488                                 $format = $default_format;
489                                 $suffix = '';
490                                 break;
491                         default:
492                                 $suffix = 0;
493                                 break;
494                 }
495                 return i18n::strftime($format, $timestamp) . $suffix;
496         }
497         
498         /**
499          * i18n::convert_locale_to_old_language_file_name()
500          * NOTE: this should be obsoleted near future.
501          * 
502          * @static
503          * @param       string  $target_locale  locale name as language_script_region
504          * @return      string  old translation file name
505          */
506         static public function convert_locale_to_old_language_file_name($target_locale)
507         {
508                 $target_language = '';
509                 foreach ( self::$lang_refs as $language => $locale )
510                 {
511                         if ( preg_match('#-#', $language) )
512                         {
513                                 if ( $target_locale . '.' . self::$charset == $locale )
514                                 {
515                                         $target_language = $language;
516                                         break;
517                                 }
518                         }
519                         else if ( $target_locale == $locale )
520                         {
521                                 $target_language = $language;
522                         }
523                 }
524                 return $target_language;
525         }
526         
527         /**
528          * i18n::convert_old_language_file_name_to_locale()
529          * NOTE: this should be obsoleted near future.
530          * 
531          * @static
532          * @param       string  $target_language        old translation file name
533          * @return      string  locale name as language_script_region
534          */
535         static public function convert_old_language_file_name_to_locale($target_language)
536         {
537                 $target_locale = '';
538                 foreach ( self::$lang_refs as $language => $locale )
539                 {
540                         if ( $target_language == $language )
541                         {
542                                 if ( preg_match('#^(.+)\.(.+)$#', $locale, $match) )
543                                 {
544                                         $target_locale = $match[1];
545                                 }
546                                 else
547                                 {
548                                         $target_locale = $locale;
549                                 }
550                                 break;
551                         }
552                 }
553                 return $target_locale;
554         }
555         
556         /**
557          * i18n::$lang_refs
558          * reference table to convert old and new way to name translation files.
559          * NOTE: this should be obsoleted as soon as possible.
560          * 
561          * @static
562          */
563         static private $lang_refs = array(
564                 "english"               => "en_Latn_US",
565                 "english-utf8"  => "en_Latn_US.UTF-8",
566                 "bulgarian"     => "bg_Cyrl_BG",
567                 "finnish"               => "fi_Latn_FU",
568                 "catalan"               => "ca_Latn_ES",
569                 "french"                => "fr_Latn_FR",
570                 "russian"               => "ru_Cyrl_RU",
571                 "chinese"               => "zh_Hans_CN",
572                 "simchinese"    => "zh_Hans_CN",
573                 "chineseb5"     => "zh_Hant_TW",
574                 "traditional_chinese"   =>      "zh_Hant_TW",
575                 "galego"                => "gl_Latn_ES",
576                 "german"                => "de_Latn_DE",
577                 "korean-utf"    => "ko_Kore_KR.UTF-8",
578                 "korean-euc-kr" => "ko_Kore_KR.EUC-KR",
579                 "slovak"                => "sk_Latn_SK",
580                 "czech"         => "cs_Latn_CZ",
581                 "hungarian"     => "hu_Latn_HU",
582                 "latvian"               => "lv_Latn_LV",
583                 "nederlands"    => "nl_Latn_NL",
584                 "italiano"              => "it_Latn_IT",
585                 "persian"               => "fa_Arab_IR",
586                 "spanish"               => "es_Latn_ES",
587                 "spanish-utf8"  => "es_Latn_ES.UTF-8",
588                 "japanese-euc"  => "ja_Jpan_JP.EUC-JP",
589                 "japanese-utf8" => "ja_Jpan_JP.UTF-8",
590                 "portuguese_brazil"     => "pt_Latn_BR"
591         );
592 }