592f1f543c5ad267079c6742ce6f4441e550586f
4 * SquirrelMail internationalization functions
6 * This file contains variuos functions that are needed to do
7 * internationalization of SquirrelMail.
9 * Internally the output character set is used. Other characters are
10 * encoded using Unicode entities according to HTML 4.0.
12 * Before 1.5.2 functions were stored in functions/i18n.php. Script is moved
13 * because it executes some code in order to detect functions supported by
14 * existing PHP installation and implements fallback functions when required
15 * functions are not available. Scripts in functions/ directory should not
16 * setup anything when they are loaded.
17 * @copyright © 1999-2007 The SquirrelMail Project Team
18 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
20 * @package squirrelmail
26 * Wrapper for textdomain(), bindtextdomain() and
27 * bind_textdomain_codeset() primarily intended for
28 * plugins when changing into their own text domain
31 * Note that plugins using this function must have
32 * their translation files located in the SquirrelMail
35 * @param string $domain_name The name of the text domain
36 * (usually the plugin name, or
37 * "squirrelmail") being switched to.
41 * @since 1.5.2 and 1.4.10
43 function sq_change_text_domain($domain_name) {
45 global $languages, $sm_notAlias;
46 static $domains_already_seen = array();
48 // only need to call bindtextdomain() once
50 if (in_array($domain_name, $domains_already_seen)) {
51 sq_textdomain($domain_name);
55 $domains_already_seen[] = $domain_name;
57 sq_bindtextdomain($domain_name, SM_PATH
. 'locale/');
58 sq_textdomain($domain_name);
60 // set codeset in order to avoid gettext charset conversions
61 if (function_exists('bind_textdomain_codeset')
62 && isset($languages[$sm_notAlias]['CHARSET'])) {
64 // Japanese translation uses different internal charset
65 if ($sm_notAlias == 'ja_JP') {
66 bind_textdomain_codeset ($domain_name, 'EUC-JP');
68 bind_textdomain_codeset ($domain_name, $languages[$sm_notAlias]['CHARSET']);
75 * Gettext bindtextdomain wrapper.
77 * Wrapper solves differences between php versions in order to provide
78 * ngettext support. Should be used if translation uses ngettext
81 * @param string $domain gettext domain name
82 * @param string $dir directory that contains all translations
83 * @return string path to translation directory
85 function sq_bindtextdomain($domain,$dir) {
86 global $l10n, $gettext_flags, $sm_notAlias;
88 if ($gettext_flags==7) {
89 // gettext extension without ngettext
90 if (substr($dir, -1) != '/') $dir .= '/';
91 $mofile=$dir . $sm_notAlias . '/LC_MESSAGES/' . $domain . '.mo';
92 $input = new FileReader($mofile);
93 $l10n[$domain] = new gettext_reader($input);
96 $dir=bindtextdomain($domain,$dir);
102 * Gettext textdomain wrapper.
103 * Makes sure that gettext_domain global is modified.
105 * @param string $name gettext domain name
106 * @return string gettext domain name
108 function sq_textdomain($domain) {
109 global $gettext_domain;
110 $gettext_domain=textdomain($domain);
111 return $gettext_domain;
115 * php setlocale function wrapper
117 * From php 4.3.0 it is possible to use arrays in order to set locale.
118 * php gettext extension works only when locale is set. This wrapper
119 * function allows to use more than one locale name.
121 * @param int $category locale category name. Use php named constants
122 * (LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME)
123 * @param mixed $locale option contains array with possible locales or string with one locale
124 * @return string name of set locale or false, if all locales fail.
125 * @since 1.5.1 and 1.4.5
126 * @see http://www.php.net/setlocale
128 function sq_setlocale($category,$locale) {
129 if (is_string($locale)) {
130 // string with only one locale
131 $ret = setlocale($category,$locale);
132 } elseif (! check_php_version(4,3)) {
133 // older php version (second setlocale argument must be string)
136 while ( ! $ret && $index<count($locale)) {
137 $ret=setlocale($category,$locale[$index]);
141 // php 4.3.0 or better, use entire array
142 $ret=setlocale($category,$locale);
146 if (preg_match("/^.*\/.*\/.*\/.*\/.*\/.*$/",$ret)) {
148 * Welcome to We-Don't-Follow-Own-Fine-Manual department
149 * OpenBSD 3.8, 3.9-current and maybe later versions
150 * return invalid response to setlocale command.
151 * SM bug report #1427512.
159 * Converts string from given charset to charset, that can be displayed by user translation.
161 * Function by default returns html encoded strings, if translation uses different encoding.
162 * If Japanese translation is used - function returns string converted to euc-jp
163 * If iconv or recode functions are enabled and translation uses utf-8 - function returns utf-8 encoded string.
164 * If $charset is not supported - function returns unconverted string.
166 * sanitizing of html tags is also done by this function.
168 * @param string $charset
169 * @param string $string Text to be decoded
170 * @param boolean $force_decode converts string to html without $charset!=$default_charset check.
171 * Argument is available since 1.5.1 and 1.4.5.
172 * @param boolean $save_html disables htmlspecialchars() in order to preserve
173 * html formating. Use with care. Available since 1.5.1
174 * @return string decoded string
176 function charset_decode ($charset, $string, $force_decode=false, $save_html=false) {
177 global $languages, $squirrelmail_language, $default_charset;
178 global $use_php_recode, $use_php_iconv, $aggressive_decoding;
180 if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
181 function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_decode')) {
182 $string = call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_decode', $string);
185 $charset = strtolower($charset);
189 // Variables that allow to use functions without function_exist() calls
190 if (! isset($use_php_recode) ||
$use_php_recode=="" ) {
191 $use_php_recode=false; }
192 if (! isset($use_php_iconv) ||
$use_php_iconv=="" ) {
193 $use_php_iconv=false; }
195 // Don't do conversion if charset is the same.
196 if ( ! $force_decode && $charset == strtolower($default_charset) )
197 return ($save_html ?
$string : htmlspecialchars($string));
199 // catch iso-8859-8-i thing
200 if ( $charset == "iso-8859-8-i" )
201 $charset = "iso-8859-8";
204 * Recode converts html special characters automatically if you use
205 * 'charset..html' decoding. There is no documented way to put -d option
206 * into php recode function call.
208 if ( $use_php_recode ) {
209 if ( $default_charset == "utf-8" ) {
210 // other charsets can be converted to utf-8 without loss.
211 // and output string is smaller
212 $string = recode_string($charset . "..utf-8",$string);
213 return ($save_html ?
$string : htmlspecialchars($string));
215 $string = recode_string($charset . "..html",$string);
216 // recode does not convert single quote, htmlspecialchars does.
217 $string = str_replace("'", ''', $string);
218 // undo html specialchars
220 $string=str_replace(array('&','"','<','>'),
221 array('&','"','<','>'),$string);
226 // iconv functions does not have html target and can be used only with utf-8
227 if ( $use_php_iconv && $default_charset=='utf-8') {
228 $string = iconv($charset,$default_charset,$string);
229 return ($save_html ?
$string : htmlspecialchars($string));
232 // If we don't use recode and iconv, we'll do it old way.
234 /* All HTML special characters are 7 bit and can be replaced first */
235 if (! $save_html) $string = htmlspecialchars ($string);
237 /* controls cpu and memory intensive decoding cycles */
238 if (! isset($aggressive_decoding) ||
$aggressive_decoding=="" ) {
239 $aggressive_decoding=false; }
241 $decode=fixcharset($charset);
242 $decodefile=SM_PATH
. 'functions/decode/' . $decode . '.php';
243 if ($decode != 'index' && file_exists($decodefile)) {
244 include_once($decodefile);
245 // send $save_html argument to decoding function. needed for iso-2022-xx decoding.
246 $ret = call_user_func('charset_decode_'.$decode, $string, $save_html);
254 * Converts html string to given charset
255 * @since 1.5.1 and 1.4.4
256 * @param string $string
257 * @param string $charset
258 * @param boolean $htmlencode keep htmlspecialchars encoding
261 function charset_encode($string,$charset,$htmlencode=true) {
262 global $default_charset;
264 $encode=fixcharset($charset);
265 $encodefile=SM_PATH
. 'functions/encode/' . $encode . '.php';
266 if ($encode != 'index' && file_exists($encodefile)) {
267 include_once($encodefile);
268 $ret = call_user_func('charset_encode_'.$encode, $string);
269 } elseif(file_exists(SM_PATH
. 'functions/encode/us_ascii.php')) {
270 // function replaces all 8bit html entities with question marks.
271 // it is used when other encoding functions are unavailable
272 include_once(SM_PATH
. 'functions/encode/us_ascii.php');
273 $ret = charset_encode_us_ascii($string);
276 * fix for yahoo users that remove all us-ascii related things
282 * Undo html special chars, some places (like compose form) have
283 * own sanitizing functions and don't need html symbols.
284 * Undo chars only after encoding in order to prevent conversion of
285 * html entities in plain text emails.
287 if (! $htmlencode ) {
288 $ret = str_replace(array('&','>','<','"'),array('&','>','<','"'),$ret);
294 * Combined decoding and encoding functions
296 * If conversion is done to charset different that utf-8, unsupported symbols
297 * will be replaced with question marks.
298 * @since 1.5.1 and 1.4.4
299 * @param string $in_charset initial charset
300 * @param string $string string that has to be converted
301 * @param string $out_charset final charset
302 * @param boolean $htmlencode keep htmlspecialchars encoding
303 * @return string converted string
305 function charset_convert($in_charset,$string,$out_charset,$htmlencode=true) {
306 $string=charset_decode($in_charset,$string,true);
307 $string=sqi18n_convert_entities($string);
308 $string=charset_encode($string,$out_charset,$htmlencode);
313 * Makes charset name suitable for decoding cycles
315 * @since 1.5.0 and 1.4.4
316 * @param string $charset Name of charset
317 * @return string $charset Adjusted name of charset
319 function fixcharset($charset) {
320 /* remove minus and characters that might be used in paths from charset
321 * name in order to be able to use it in function names and include calls.
323 $charset=preg_replace("/[-:.\/\\\]/",'_',$charset);
325 // OE ks_c_5601_1987 > cp949
326 $charset=str_replace('ks_c_5601_1987','cp949',$charset);
327 // Moz x-euc-tw > euc-tw
328 $charset=str_replace('x_euc','euc',$charset);
329 // Moz x-windows-949 > cp949
330 $charset=str_replace('x_windows_','cp',$charset);
332 // windows-125x and cp125x charsets
333 $charset=str_replace('windows_','cp',$charset);
336 $charset=str_replace('ibm','cp',$charset);
338 // iso-8859-8-i -> iso-8859-8
339 // use same cycle until I'll find differences
340 $charset=str_replace('iso_8859_8_i','iso_8859_8',$charset);
346 * Set up the language to be output
347 * if $do_search is true, then scan the browser information
348 * for a possible language that we know
350 * Function sets system locale environment (LC_ALL, LANG, LANGUAGE),
351 * gettext translation bindings and html header information.
353 * Function returns error codes, if there is some fatal error.
355 * 1 = mbstring support is not present,
356 * 2 = mbstring support is not present, user's translation reverted to en_US.
358 * @param string $sm_language translation used by user's interface
359 * @param bool $do_search use browser's preferred language detection functions. Defaults to false.
360 * @param bool $default set $sm_language to $squirrelmail_default_language if language detection fails or language is not set. Defaults to false.
361 * @return int function execution error codes.
363 function set_up_language($sm_language, $do_search = false, $default = false) {
365 static $SetupAlready = 0;
366 global $use_gettext, $languages,
367 $squirrelmail_language, $squirrelmail_default_language, $default_charset,
368 $sm_notAlias, $username, $data_dir;
374 $SetupAlready = TRUE;
375 sqgetGlobalVar('HTTP_ACCEPT_LANGUAGE', $accept_lang, SQ_SERVER
);
378 * If function is asked to detect preferred language
379 * OR squirrelmail default language is set to empty string
381 * squirrelmail language ($sm_language) is empty string
382 * (not set in user's prefs and no cookie with language info)
384 * browser provides list of preferred languages
386 * get preferred language from HTTP_ACCEPT_LANGUAGE header
388 if (($do_search ||
empty($squirrelmail_default_language)) &&
390 isset($accept_lang)) {
391 // TODO: use more than one language, if first language is not available
392 // FIXME: function assumes that string contains two or more characters.
393 // FIXME: some languages use 5 chars
394 $sm_language = substr($accept_lang, 0, 2);
398 * If language preference is not set OR script asks to use default language
400 * default squirrelmail language is not set to empty string
402 * use default squirrelmail language value from configuration.
404 if ((!$sm_language||
$default) &&
405 ! empty($squirrelmail_default_language)) {
406 $squirrelmail_language = $squirrelmail_default_language;
407 $sm_language = $squirrelmail_default_language;
410 /** provide failsafe language when detection fails */
411 if (! $sm_language) $sm_language='en_US';
413 $sm_notAlias = $sm_language;
415 // Catching removed translation
416 // System reverts to English translation if user prefs contain translation
417 // that is not available in $languages array
418 if (!isset($languages[$sm_notAlias])) {
419 $sm_notAlias="en_US";
422 while (isset($languages[$sm_notAlias]['ALIAS'])) {
423 $sm_notAlias = $languages[$sm_notAlias]['ALIAS'];
426 if ( isset($sm_language) &&
428 $sm_language != '' &&
429 isset($languages[$sm_notAlias]['CHARSET']) ) {
430 sq_bindtextdomain( 'squirrelmail', SM_PATH
. 'locale/' );
431 sq_textdomain( 'squirrelmail' );
433 // set codeset in order to avoid gettext charset conversions
434 if (function_exists('bind_textdomain_codeset')) {
435 // Japanese translation uses different internal charset
436 if ($sm_notAlias == 'ja_JP') {
437 bind_textdomain_codeset ('squirrelmail', 'EUC-JP');
439 bind_textdomain_codeset ('squirrelmail', $languages[$sm_notAlias]['CHARSET'] );
443 // Use LOCALE key, if it is set.
444 if (isset($languages[$sm_notAlias]['LOCALE'])){
445 $longlocale=$languages[$sm_notAlias]['LOCALE'];
447 $longlocale=$sm_notAlias;
450 // try setting locale
451 $retlocale=sq_setlocale(LC_ALL
, $longlocale);
453 // check if locale is set and assign that locale to $longlocale
454 // in order to use it in putenv calls.
455 if (! is_bool($retlocale)) {
456 $longlocale=$retlocale;
457 } elseif (is_array($longlocale)) {
458 // setting of all locales failed.
459 // we need string instead of array used in LOCALE key.
460 $longlocale=$sm_notAlias;
463 if ( !((bool)ini_get('safe_mode')) &&
464 getenv( 'LC_ALL' ) != $longlocale ) {
465 putenv( "LC_ALL=$longlocale" );
466 putenv( "LANG=$longlocale" );
467 putenv( "LANGUAGE=$longlocale" );
468 putenv( "LC_NUMERIC=C" );
469 if ($sm_notAlias=='tr_TR') putenv( "LC_CTYPE=C" );
471 // Workaround for plugins that use numbers with floating point
472 // It might be removed if plugins use correct decimal delimiters
473 // according to locale settings.
474 setlocale(LC_NUMERIC
, 'C');
475 // Workaround for specific Turkish strtolower/strtoupper rules.
476 // Many functions expect English conversion rules.
477 if ($sm_notAlias=='tr_TR') setlocale(LC_CTYPE
,'C');
480 * Set text direction/alignment variables
481 * When language environment is setup, scripts can use these globals
482 * without accessing $languages directly and making checks for optional
485 global $text_direction, $left_align, $right_align;
486 if (isset($languages[$sm_notAlias]['DIR']) &&
487 $languages[$sm_notAlias]['DIR'] == 'rtl') {
490 * @global string $text_direction
492 $text_direction='rtl';
495 * @global string $left_align
500 * @global string $right_align
504 $text_direction='ltr';
506 $right_align='right';
509 $squirrelmail_language = $sm_notAlias;
510 if ($squirrelmail_language == 'ja_JP') {
511 header ('Content-Type: text/html; charset=EUC-JP');
512 if (!function_exists('mb_internal_encoding')) {
513 // Error messages can't be displayed here
515 // Revert to English if possible.
516 if (function_exists('setPref') && $username!='' && $data_dir!="") {
517 setPref($data_dir, $username, 'language', "en_US");
520 // stop further execution in order not to get php errors on mb_internal_encoding().
523 if (function_exists('mb_language')) {
524 mb_language('Japanese');
526 mb_internal_encoding('EUC-JP');
527 mb_http_output('pass');
528 } elseif ($squirrelmail_language == 'en_US') {
529 header( 'Content-Type: text/html; charset=' . $default_charset );
531 header( 'Content-Type: text/html; charset=' . $languages[$sm_notAlias]['CHARSET'] );
534 * mbstring.func_overload fix (#929644).
536 * php mbstring extension can replace standard string functions with their multibyte
537 * equivalents. See http://www.php.net/ref.mbstring#mbstring.overload. This feature
538 * was added in php v.4.2.0
540 * Some SquirrelMail functions work with 8bit strings in bytes. If interface is forced
541 * to use mbstring functions and mbstring internal encoding is set to multibyte charset,
542 * interface can't trust regular string functions. Due to mbstring overloading design
543 * limits php scripts can't control this setting.
545 * This hack should fix some issues related to 8bit strings in passwords. Correct fix is
546 * to disable mbstring overloading. Japanese translation uses different internal encoding.
548 if ($squirrelmail_language != 'ja_JP' &&
549 function_exists('mb_internal_encoding') &&
550 check_php_version(4,2,0) &&
551 (int)ini_get('mbstring.func_overload')!=0) {
552 mb_internal_encoding('pass');
559 * Sets default_charset variable according to the one that is used by user's translations.
561 * Function changes global $default_charset variable in order to be sure, that it
562 * contains charset used by user's translation. Sanity of $squirrelmail_language
563 * and $default_charset combination is also tested.
565 * There can be a $default_charset setting in the
566 * config.php file, but the user may have a different language
567 * selected for a user interface. This function checks the
568 * language selected by the user and tags the outgoing messages
569 * with the appropriate charset corresponding to the language
570 * selection. This is "more right" (tm), than just stamping the
571 * message blindly with the system-wide $default_charset.
573 function set_my_charset(){
574 global $data_dir, $username, $default_charset, $languages, $squirrelmail_language;
576 $my_language = getPref($data_dir, $username, 'language');
578 $my_language = $squirrelmail_language ;
580 // Catch removed translation
581 if (!isset($languages[$my_language])) {
582 $my_language="en_US";
584 while (isset($languages[$my_language]['ALIAS'])) {
585 $my_language = $languages[$my_language]['ALIAS'];
587 $my_charset = $languages[$my_language]['CHARSET'];
588 if ($my_language!='en_US') {
589 $default_charset = $my_charset;
594 * Replaces non-braking spaces inserted by some browsers with regular space
596 * This function can be used to replace non-braking space symbols
597 * that are inserted in forms by some browsers instead of normal
600 * @param string $string Text that needs to be cleaned
601 * @param string $charset Charset used in text
602 * @return string Cleaned text
604 function cleanup_nbsp($string,$charset) {
606 // reduce number of case statements
607 if (stristr('iso-8859-',substr($charset,0,9))){
608 $output_charset="iso-8859-x";
610 if (stristr('windows-125',substr($charset,0,11))){
611 $output_charset="cp125x";
613 if (stristr('koi8',substr($charset,0,4))){
614 $output_charset="koi8-x";
616 if (! isset($output_charset)){
617 $output_charset=strtolower($charset);
620 // where is non-braking space symbol
621 switch($output_charset):
634 // don't change string if charset is unmatched
638 // return space instead of non-braking space.
639 return str_replace($nbsp,' ',$string);
643 * Function informs if it is safe to convert given charset to the one that is used by user.
645 * It is safe to use conversion only if user uses utf-8 encoding and when
646 * converted charset is similar to the one that is used by user.
648 * @param string $input_charset Charset of text that needs to be converted
649 * @return bool is it possible to convert to user's charset
651 function is_conversion_safe($input_charset) {
652 global $languages, $sm_notAlias, $default_charset, $lossy_encoding;
654 if (isset($lossy_encoding) && $lossy_encoding )
657 // convert to lower case
658 $input_charset = strtolower($input_charset);
660 // Is user's locale Unicode based ?
661 if ( $default_charset == "utf-8" ) {
665 // Charsets that are similar
666 switch ($default_charset) {
668 if ( $input_charset == "iso-8859-5" ||
669 $input_charset == "koi8-r" ||
670 $input_charset == "koi8-u" ) {
676 if ( $input_charset == "iso-8859-13" ||
677 $input_charset == "iso-8859-4" ) {
683 if ( $input_charset == "iso-8859-13" ||
684 $input_charset == "windows-1257" ) {
690 if ( $input_charset == "windows-1251" ||
691 $input_charset == "koi8-r" ||
692 $input_charset == "koi8-u" ) {
698 if ( $input_charset == "iso-8859-4" ||
699 $input_charset == "windows-1257" ) {
705 if ( $input_charset == "windows-1251" ||
706 $input_charset == "iso-8859-5" ||
707 $input_charset == "koi8-u" ) {
713 if ( $input_charset == "windows-1251" ||
714 $input_charset == "iso-8859-5" ||
715 $input_charset == "koi8-r" ) {
726 * Converts html character entities to numeric entities
728 * SquirrelMail encoding functions work only with numeric entities.
729 * This function fixes issues with decoding functions that might convert
730 * some symbols to character entities. Issue is specific to PHP recode
731 * extension decoding. Function is used internally in charset_convert()
733 * @param string $str string that might contain html character entities
734 * @return string string with character entities converted to decimals.
737 function sqi18n_convert_entities($str) {
741 ' ' => ' ',
742 '¡' => '¡',
743 '¢' => '¢',
744 '£' => '£',
745 '¤' => '¤',
747 '¦' => '¦',
748 '§' => '§',
750 '©' => '©',
751 'ª' => 'ª',
752 '«' => '«',
756 '¯' => '¯',
758 '±' => '±',
759 '²' => '²',
760 '³' => '³',
761 '´' => '´',
762 'µ' => 'µ',
763 '¶' => '¶',
764 '·' => '·',
765 '¸' => '¸',
766 '¹' => '¹',
767 'º' => 'º',
768 '»' => '»',
769 '¼' => '¼',
770 '½' => '½',
771 '¾' => '¾',
772 '¿' => '¿',
773 'À' => 'À',
774 'Á' => 'Á',
775 'Â' => 'Â',
776 'Ã' => 'Ã',
777 'Ä' => 'Ä',
778 'Å' => 'Å',
779 'Æ' => 'Æ',
780 'Ç' => 'Ç',
781 'È' => 'È',
782 'É' => 'É',
783 'Ê' => 'Ê',
784 'Ë' => 'Ë',
785 'Ì' => 'Ì',
786 'Í' => 'Í',
787 'Î' => 'Î',
788 'Ï' => 'Ï',
790 'Ñ' => 'Ñ',
791 'Ò' => 'Ò',
792 'Ó' => 'Ó',
793 'Ô' => 'Ô',
794 'Õ' => 'Õ',
795 'Ö' => 'Ö',
796 '×' => '×',
797 'Ø' => 'Ø',
798 'Ù' => 'Ù',
799 'Ú' => 'Ú',
800 'Û' => 'Û',
801 'Ü' => 'Ü',
802 'Ý' => 'Ý',
803 'Þ' => 'Þ',
804 'ß' => 'ß',
805 'à' => 'à',
806 'á' => 'á',
807 'â' => 'â',
808 'ã' => 'ã',
809 'ä' => 'ä',
810 'å' => 'å',
811 'æ' => 'æ',
812 'ç' => 'ç',
813 'è' => 'è',
814 'é' => 'é',
815 'ê' => 'ê',
816 'ë' => 'ë',
817 'ì' => 'ì',
818 'í' => 'í',
819 'î' => 'î',
820 'ï' => 'ï',
822 'ñ' => 'ñ',
823 'ò' => 'ò',
824 'ó' => 'ó',
825 'ô' => 'ô',
826 'õ' => 'õ',
827 'ö' => 'ö',
828 '÷' => '÷',
829 'ø' => 'ø',
830 'ù' => 'ù',
831 'ú' => 'ú',
832 'û' => 'û',
833 'ü' => 'ü',
834 'ý' => 'ý',
835 'þ' => 'þ',
836 'ÿ' => 'ÿ',
838 'Œ' => 'Œ',
839 'œ' => 'œ',
840 'Š' => 'Š',
841 'š' => 'š',
842 'Ÿ' => 'Ÿ',
843 // Spacing Modifier Letters
844 'ˆ' => 'ˆ',
845 '˜' => '˜',
846 // General Punctuation
847 ' ' => ' ',
848 ' ' => ' ',
849 ' ' => ' ',
850 '‌' => '‌',
851 '‍' => '‍',
852 '‎' => '‎',
853 '‏' => '‏',
854 '–' => '–',
855 '—' => '—',
856 '‘' => '‘',
857 '’' => '’',
858 '‚' => '‚',
859 '“' => '“',
860 '”' => '”',
861 '„' => '„',
862 '†' => '†',
863 '‡' => '‡',
864 '‰' => '‰',
865 '‹' => '‹',
866 '›' => '›',
867 '€' => '€',
869 'ƒ' => 'ƒ',
871 'Α' => 'Α',
872 'Β' => 'Β',
873 'Γ' => 'Γ',
874 'Δ' => 'Δ',
875 'Ε' => 'Ε',
876 'Ζ' => 'Ζ',
878 'Θ' => 'Θ',
879 'Ι' => 'Ι',
880 'Κ' => 'Κ',
881 'Λ' => 'Λ',
885 'Ο' => 'Ο',
888 'Σ' => 'Σ',
890 'Υ' => 'Υ',
894 'Ω' => 'Ω',
895 'α' => 'α',
896 'β' => 'β',
897 'γ' => 'γ',
898 'δ' => 'δ',
899 'ε' => 'ε',
900 'ζ' => 'ζ',
902 'θ' => 'θ',
903 'ι' => 'ι',
904 'κ' => 'κ',
905 'λ' => 'λ',
909 'ο' => 'ο',
912 'ς' => 'ς',
913 'σ' => 'σ',
915 'υ' => 'υ',
919 'ω' => 'ω',
920 'ϑ' => 'ϑ',
921 'ϒ' => 'ϒ',
923 // General Punctuation
924 '•' => '•',
925 '…' => '…',
926 '′' => '′',
927 '″' => '″',
928 '‾' => '‾',
929 '⁄' => '⁄',
930 // Letterlike Symbols
931 '℘' => '℘',
932 'ℑ' => 'ℑ',
933 'ℜ' => 'ℜ',
934 '™' => '™',
935 'ℵ' => 'ℵ',
937 '←' => '←',
938 '↑' => '↑',
939 '→' => '→',
940 '↓' => '↓',
941 '↔' => '↔',
942 '↵' => '↵',
943 '⇐' => '⇐',
944 '⇑' => '⇑',
945 '⇒' => '⇒',
946 '⇓' => '⇓',
947 '⇔' => '⇔',
948 // Mathematical Operators
949 '∀' => '∀',
950 '∂' => '∂',
951 '∃' => '∃',
952 '∅' => '∅',
953 '∇' => '∇',
954 '∈' => '∈',
955 '∉' => '∉',
957 '∏' => '∏',
958 '∑' => '∑',
959 '−' => '−',
960 '∗' => '∗',
961 '√' => '√',
962 '∝' => '∝',
963 '∞' => '∞',
964 '∠' => '∠',
965 '∧' => '∧',
967 '∩' => '∩',
968 '∪' => '∪',
969 '∫' => '∫',
970 '∴' => '∴',
971 '∼' => '∼',
972 '≅' => '≅',
973 '≈' => '≈',
975 '≡' => '≡',
978 '⊂' => '⊂',
979 '⊃' => '⊃',
980 '⊄' => '⊄',
981 '⊆' => '⊆',
982 '⊇' => '⊇',
983 '⊕' => '⊕',
984 '⊗' => '⊗',
985 '⊥' => '⊥',
986 '⋅' => '⋅',
987 // Miscellaneous Technical
988 '⌈' => '⌈',
989 '⌉' => '⌉',
990 '⌊' => '⌊',
991 '⌋' => '⌋',
992 '⟨' => '〈',
993 '⟩' => '〉',
995 '◊' => '◊',
996 // Miscellaneous Symbols
997 '♠' => '♠',
998 '♣' => '♣',
999 '♥' => '♥',
1000 '♦' => '♦');
1002 $str = str_replace(array_keys($entities), array_values($entities), $str);
1007 /* ------------------------------ main --------------------------- */
1009 global $squirrelmail_language, $languages, $use_gettext;
1011 if (! sqgetGlobalVar('squirrelmail_language',$squirrelmail_language,SQ_COOKIE
)) {
1012 $squirrelmail_language = '';
1016 * Array specifies the available translations.
1018 * Structure of array:
1019 * $languages['language']['variable'] = 'value'
1021 * Possible 'variable' names:
1022 * NAME - Translation name in English
1023 * CHARSET - Encoding used by translation
1024 * ALIAS - used when 'language' is only short name and 'value' should provide long language name
1025 * ALTNAME - Native translation name. Any 8bit symbols must be html encoded.
1026 * LOCALE - Full locale name (in xx_XX.charset format). It can use array with more than one locale name since 1.4.5 and 1.5.1
1027 * DIR - Text direction. Used to define Right-to-Left languages. Possible values 'rtl' or 'ltr'. If undefined - defaults to 'ltr'
1028 * XTRA_CODE - translation uses special functions. See doc/i18n.txt
1030 * Each 'language' definition requires NAME+CHARSET or ALIAS variables.
1033 * @global array $languages
1035 $languages['en_US']['NAME'] = 'English';
1036 $languages['en_US']['CHARSET'] = 'iso-8859-1';
1037 $languages['en_US']['LOCALE'] = 'en_US.ISO8859-1';
1038 $languages['en']['ALIAS'] = 'en_US';
1041 * Automatic translation loading from setup.php files.
1042 * Solution for bug. 1240889.
1043 * setup.php file can contain $languages array entries and XTRA_CODE functions.
1045 if (is_dir(SM_PATH
. 'locale') &&
1046 is_readable(SM_PATH
. 'locale')) {
1047 $localedir = dir(SM_PATH
. 'locale');
1048 while($lang_dir=$localedir->read()) {
1049 // remove trailing slash, if present
1050 if (substr($lang_dir,-1)=='/') {
1051 $lang_dir = substr($lang_dir,0,-1);
1053 if ($lang_dir != '..' && $lang_dir != '.' && $lang_dir != 'CVS' &&
1054 $lang_dir != '.svn' && is_dir(SM_PATH
.'locale/'.$lang_dir) &&
1055 file_exists(SM_PATH
.'locale/'.$lang_dir.'/setup.php')) {
1056 include_once(SM_PATH
.'locale/'.$lang_dir.'/setup.php');
1059 $localedir->close();
1062 /* Detect whether gettext is installed. */
1064 if (function_exists('_')) {
1065 $gettext_flags +
= 1;
1067 if (function_exists('bindtextdomain')) {
1068 $gettext_flags +
= 2;
1070 if (function_exists('textdomain')) {
1071 $gettext_flags +
= 4;
1073 if (function_exists('ngettext')) {
1074 $gettext_flags +
= 8;
1077 /* If gettext is fully loaded, cool */
1078 if ($gettext_flags == 15) {
1079 $use_gettext = true;
1082 /* If ngettext support is missing, load it */
1083 elseif ($gettext_flags == 7) {
1084 $use_gettext = true;
1085 // load internal ngettext functions
1086 include_once(SM_PATH
. 'class/l10n.class.php');
1087 include_once(SM_PATH
. 'functions/ngettext.php');
1090 /* If we can fake gettext, try that */
1091 elseif ($gettext_flags == 0) {
1092 $use_gettext = true;
1093 include_once(SM_PATH
. 'functions/gettext.php');
1095 /* Uh-ho. A weird install */
1096 if (! $gettext_flags & 1) {
1098 * Function is used as replacement in broken installs
1105 if (! $gettext_flags & 2) {
1107 * Function is used as replacement in broken installs
1110 function bindtextdomain() {
1114 if (! $gettext_flags & 4) {
1116 * Function is used as replacemet in broken installs
1119 function textdomain() {
1123 if (! $gettext_flags & 8) {
1125 * Function is used as replacemet in broken installs
1128 function ngettext($str,$str2,$number) {
1136 if (! function_exists('dgettext')) {
1138 * Replacement for broken setups.
1141 function dgettext($domain,$str) {
1145 if (! function_exists('dngettext')) {
1147 * Replacement for broken setups
1150 function dngettext($domain,$str1,$strn,$number) {
1151 return ($number==1 ?
$str1 : $strn);