Source for file languages.php

Documentation is available at languages.php

  1. <?php
  2.  
  3. /**
  4.  * SquirrelMail internationalization functions
  5.  *
  6.  * This file contains variuos functions that are needed to do
  7.  * internationalization of SquirrelMail.
  8.  *
  9.  * Internally the output character set is used. Other characters are
  10.  * encoded using Unicode entities according to HTML 4.0.
  11.  *
  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 &copy; 1999-2006 The SquirrelMail Project Team
  18.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  19.  * @version $Id: languages.php,v 1.5 2006/08/06 08:46:36 tokul Exp $
  20.  * @package squirrelmail
  21.  * @subpackage i18n
  22.  */
  23.  
  24.  
  25. /**
  26.  * Gettext bindtextdomain wrapper.
  27.  *
  28.  * Wrapper solves differences between php versions in order to provide
  29.  * ngettext support. Should be used if translation uses ngettext
  30.  * functions.
  31.  * @since 1.5.1
  32.  * @param string $domain gettext domain name
  33.  * @param string $dir directory that contains all translations
  34.  * @return string path to translation directory
  35.  */
  36. function sq_bindtextdomain($domain,$dir{
  37.     global $l10n$gettext_flags$sm_notAlias;
  38.  
  39.     if ($gettext_flags==7{
  40.         // gettext extension without ngettext
  41.         if (substr($dir-1!= '/'$dir .= '/';
  42.         $mofile=$dir $sm_notAlias '/LC_MESSAGES/' $domain '.mo';
  43.         $input new FileReader($mofile);
  44.         $l10n[$domainnew gettext_reader($input);
  45.     }
  46.  
  47.     $dir=bindtextdomain($domain,$dir);
  48.  
  49.     return $dir;
  50. }
  51.  
  52. /**
  53.  * Gettext textdomain wrapper.
  54.  * Makes sure that gettext_domain global is modified.
  55.  * @since 1.5.1
  56.  * @param string $name gettext domain name
  57.  * @return string gettext domain name
  58.  */
  59. function sq_textdomain($domain{
  60.     global $gettext_domain;
  61.     $gettext_domain=textdomain($domain);
  62.     return $gettext_domain;
  63. }
  64.  
  65. /**
  66.  * php setlocale function wrapper
  67.  *
  68.  * From php 4.3.0 it is possible to use arrays in order to set locale.
  69.  * php gettext extension works only when locale is set. This wrapper
  70.  * function allows to use more than one locale name.
  71.  *
  72.  * @param int $category locale category name. Use php named constants
  73.  *      (LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME)
  74.  * @param mixed $locale option contains array with possible locales or string with one locale
  75.  * @return string name of set locale or false, if all locales fail.
  76.  * @since 1.5.1 and 1.4.5
  77.  * @see http://www.php.net/setlocale
  78.  */
  79. function sq_setlocale($category,$locale{
  80.     if (is_string($locale)) {
  81.         // string with only one locale
  82.         $ret setlocale($category,$locale);
  83.     elseif (check_php_version(4,3)) {
  84.         // older php version (second setlocale argument must be string)
  85.         $ret=false;
  86.         $index=0;
  87.         while $ret && $index<count($locale)) {
  88.             $ret=setlocale($category,$locale[$index]);
  89.             $index++;
  90.         }
  91.     else {
  92.         // php 4.3.0 or better, use entire array
  93.         $ret=setlocale($category,$locale);
  94.     }
  95.  
  96.     /* safety checks */
  97.     if (preg_match("/^.*\/.*\/.*\/.*\/.*\/.*$/",$ret)) {
  98.         /**
  99.          * Welcome to We-Don't-Follow-Own-Fine-Manual department
  100.          * OpenBSD 3.8, 3.9-current and maybe later versions 
  101.          * return invalid response to setlocale command.
  102.          * SM bug report #1427512.
  103.          */
  104.         $ret false;
  105.     }
  106.     return $ret;
  107. }
  108.  
  109. /**
  110.  * Converts string from given charset to charset, that can be displayed by user translation.
  111.  *
  112.  * Function by default returns html encoded strings, if translation uses different encoding.
  113.  * If Japanese translation is used - function returns string converted to euc-jp
  114.  * If iconv or recode functions are enabled and translation uses utf-8 - function returns utf-8 encoded string.
  115.  * If $charset is not supported - function returns unconverted string.
  116.  *
  117.  * sanitizing of html tags is also done by this function.
  118.  *
  119.  * @param string $charset 
  120.  * @param string $string Text to be decoded
  121.  * @param boolean $force_decode converts string to html without $charset!=$default_charset check.
  122.  *  Argument is available since 1.5.1 and 1.4.5.
  123.  * @param boolean $save_html disables htmlspecialchars() in order to preserve
  124.  *   html formating. Use with care. Available since 1.5.1
  125.  * @return string decoded string
  126.  */
  127. function charset_decode ($charset$string$force_decode=false$save_html=false{
  128.     global $languages$squirrelmail_language$default_charset;
  129.  
  130.     if (isset($languages[$squirrelmail_language]['XTRA_CODE']&&
  131.         function_exists($languages[$squirrelmail_language]['XTRA_CODE''_decode')) {
  132.         $string call_user_func($languages[$squirrelmail_language]['XTRA_CODE''_decode'$string);
  133.     }
  134.  
  135.     $charset strtolower($charset);
  136.  
  137.     set_my_charset();
  138.  
  139.     // Variables that allow to use functions without function_exist() calls
  140.     if (isset($use_php_recode|| $use_php_recode=="" {
  141.         $use_php_recode=false}
  142.     if (isset($use_php_iconv|| $use_php_iconv=="" {
  143.         $use_php_iconv=false}
  144.  
  145.     // Don't do conversion if charset is the same.
  146.     if $force_decode && $charset == strtolower($default_charset) )
  147.         return ($save_html $string htmlspecialchars($string));
  148.  
  149.     // catch iso-8859-8-i thing
  150.     if $charset == "iso-8859-8-i" )
  151.         $charset "iso-8859-8";
  152.  
  153.     /*
  154.      * Recode converts html special characters automatically if you use
  155.      * 'charset..html' decoding. There is no documented way to put -d option
  156.      * into php recode function call.
  157.      */
  158.     if $use_php_recode {
  159.         if $default_charset == "utf-8" {
  160.             // other charsets can be converted to utf-8 without loss.
  161.             // and output string is smaller
  162.             $string recode_string($charset "..utf-8",$string);
  163.             return ($save_html $string htmlspecialchars($string));
  164.         else {
  165.             $string recode_string($charset "..html",$string);
  166.             // recode does not convert single quote, htmlspecialchars does.
  167.             $string str_replace("'"'&#039;'$string);
  168.             // undo html specialchars
  169.             if ($save_html)
  170.                 $string=str_replace(array('&amp;','&quot;','&lt;','&gt;'),
  171.                                     array('&','"','<','>'),$string);
  172.             return $string;
  173.         }
  174.     }
  175.  
  176.     // iconv functions does not have html target and can be used only with utf-8
  177.     if $use_php_iconv && $default_charset=='utf-8'{
  178.         $string iconv($charset,$default_charset,$string);
  179.         return ($save_html $string htmlspecialchars($string));
  180.     }
  181.  
  182.     // If we don't use recode and iconv, we'll do it old way.
  183.  
  184.     /* All HTML special characters are 7 bit and can be replaced first */
  185.     if ($save_html$string htmlspecialchars ($string);
  186.  
  187.     /* controls cpu and memory intensive decoding cycles */
  188.     if (isset($aggressive_decoding|| $aggressive_decoding=="" {
  189.         $aggressive_decoding=false}
  190.  
  191.     $decode=fixcharset($charset);
  192.     $decodefile=SM_PATH 'functions/decode/' $decode '.php';
  193.     if (file_exists($decodefile)) {
  194.         include_once($decodefile);
  195.         // send $save_html argument to decoding function. needed for iso-2022-xx decoding.
  196.         $ret call_user_func('charset_decode_'.$decode$string$save_html);
  197.     else {
  198.         $ret $string;
  199.     }
  200.     return$ret );
  201. }
  202.  
  203. /**
  204.  * Converts html string to given charset
  205.  * @since 1.5.1 and 1.4.4
  206.  * @param string $string 
  207.  * @param string $charset 
  208.  * @param boolean $htmlencode keep htmlspecialchars encoding
  209.  * @return string 
  210.  */
  211. function charset_encode($string,$charset,$htmlencode=true{
  212.     global $default_charset;
  213.  
  214.     $encode=fixcharset($charset);
  215.     $encodefile=SM_PATH 'functions/encode/' $encode '.php';
  216.     if (file_exists($encodefile)) {
  217.         include_once($encodefile);
  218.         $ret call_user_func('charset_encode_'.$encode$string);
  219.     elseif(file_exists(SM_PATH 'functions/encode/us_ascii.php')) {
  220.         // function replaces all 8bit html entities with question marks.
  221.         // it is used when other encoding functions are unavailable
  222.         include_once(SM_PATH 'functions/encode/us_ascii.php');
  223.         $ret charset_encode_us_ascii($string);
  224.     else {
  225.         /**
  226.          * fix for yahoo users that remove all us-ascii related things
  227.          */
  228.         $ret $string;
  229.     }
  230.  
  231.     /**
  232.      * Undo html special chars, some places (like compose form) have
  233.      * own sanitizing functions and don't need html symbols.
  234.      * Undo chars only after encoding in order to prevent conversion of
  235.      * html entities in plain text emails.
  236.      */
  237.     if ($htmlencode {
  238.         $ret str_replace(array('&amp;','&gt;','&lt;','&quot;'),array('&','>','<','"'),$ret);
  239.     }
  240.     return$ret );
  241. }
  242.  
  243. /**
  244.  * Combined decoding and encoding functions
  245.  *
  246.  * If conversion is done to charset different that utf-8, unsupported symbols
  247.  * will be replaced with question marks.
  248.  * @since 1.5.1 and 1.4.4
  249.  * @param string $in_charset initial charset
  250.  * @param string $string string that has to be converted
  251.  * @param string $out_charset final charset
  252.  * @param boolean $htmlencode keep htmlspecialchars encoding
  253.  * @return string converted string
  254.  */
  255. function charset_convert($in_charset,$string,$out_charset,$htmlencode=true{
  256.     $string=charset_decode($in_charset,$string,true);
  257.     $string=sqi18n_convert_entities($string);
  258.     $string=charset_encode($string,$out_charset,$htmlencode);
  259.     return $string;
  260. }
  261.  
  262. /**
  263.  * Makes charset name suitable for decoding cycles
  264.  *
  265.  * @since 1.5.0 and 1.4.4
  266.  * @param string $charset Name of charset
  267.  * @return string $charset Adjusted name of charset
  268.  */
  269. function fixcharset($charset{
  270.     /* remove minus and characters that might be used in paths from charset
  271.      * name in order to be able to use it in function names and include calls.
  272.      */
  273.     $charset=preg_replace("/[-:.\/\\\]/",'_',$charset);
  274.  
  275.     // OE ks_c_5601_1987 > cp949
  276.     $charset=str_replace('ks_c_5601_1987','cp949',$charset);
  277.     // Moz x-euc-tw > euc-tw
  278.     $charset=str_replace('x_euc','euc',$charset);
  279.     // Moz x-windows-949 > cp949
  280.     $charset=str_replace('x_windows_','cp',$charset);
  281.  
  282.     // windows-125x and cp125x charsets
  283.     $charset=str_replace('windows_','cp',$charset);
  284.  
  285.     // ibm > cp
  286.     $charset=str_replace('ibm','cp',$charset);
  287.  
  288.     // iso-8859-8-i -> iso-8859-8
  289.     // use same cycle until I'll find differences
  290.     $charset=str_replace('iso_8859_8_i','iso_8859_8',$charset);
  291.  
  292.     return $charset;
  293. }
  294.  
  295. /**
  296.  * Set up the language to be output
  297.  * if $do_search is true, then scan the browser information
  298.  * for a possible language that we know
  299.  *
  300.  * Function sets system locale environment (LC_ALL, LANG, LANGUAGE),
  301.  * gettext translation bindings and html header information.
  302.  *
  303.  * Function returns error codes, if there is some fatal error.
  304.  *  0 = no error,
  305.  *  1 = mbstring support is not present,
  306.  *  2 = mbstring support is not present, user's translation reverted to en_US.
  307.  *
  308.  * @param string $sm_language translation used by user's interface
  309.  * @param bool $do_search use browser's preferred language detection functions. Defaults to false.
  310.  * @param bool $default set $sm_language to $squirrelmail_default_language if language detection fails or language is not set. Defaults to false.
  311.  * @return int function execution error codes.
  312.  */
  313. function set_up_language($sm_language$do_search false$default false{
  314.  
  315.     static $SetupAlready 0;
  316.     global $use_gettext$languages,
  317.            $squirrelmail_language$squirrelmail_default_language$default_charset,
  318.            $sm_notAlias$username$data_dir;
  319.  
  320.     if ($SetupAlready{
  321.         return;
  322.     }
  323.  
  324.     $SetupAlready TRUE;
  325.     sqgetGlobalVar('HTTP_ACCEPT_LANGUAGE',  $accept_langSQ_SERVER);
  326.  
  327.     /**
  328.      * If function is asked to detect preferred language
  329.      *  OR squirrelmail default language is set to empty string
  330.      *    AND
  331.      * squirrelmail language ($sm_language) is empty string
  332.      * (not set in user's prefs and no cookie with language info)
  333.      *    AND
  334.      * browser provides list of preferred languages
  335.      *  THEN
  336.      * get preferred language from HTTP_ACCEPT_LANGUAGE header
  337.      */
  338.     if (($do_search || empty($squirrelmail_default_language)) &&
  339.         $sm_language &&
  340.         isset($accept_lang)) {
  341.         // TODO: use more than one language, if first language is not available
  342.         // FIXME: function assumes that string contains two or more characters.
  343.         // FIXME: some languages use 5 chars
  344.         $sm_language substr($accept_lang02);
  345.     }
  346.  
  347.     /**
  348.      * If language preference is not set OR script asks to use default language
  349.      *  AND
  350.      * default squirrelmail language is not set to empty string
  351.      *  THEN
  352.      * use default squirrelmail language value from configuration.
  353.      */
  354.     if ((!$sm_language||$default&&
  355.         empty($squirrelmail_default_language)) {
  356.         $squirrelmail_language $squirrelmail_default_language;
  357.         $sm_language $squirrelmail_default_language;
  358.     }
  359.  
  360.     /** provide failsafe language when detection fails */
  361.     if ($sm_language$sm_language='en_US';
  362.  
  363.     $sm_notAlias $sm_language;
  364.  
  365.     // Catching removed translation
  366.     // System reverts to English translation if user prefs contain translation
  367.     // that is not available in $languages array
  368.     if (!isset($languages[$sm_notAlias])) {
  369.         $sm_notAlias="en_US";
  370.     }
  371.  
  372.     while (isset($languages[$sm_notAlias]['ALIAS'])) {
  373.         $sm_notAlias $languages[$sm_notAlias]['ALIAS'];
  374.     }
  375.  
  376.     if isset($sm_language&&
  377.          $use_gettext &&
  378.          $sm_language != '' &&
  379.          isset($languages[$sm_notAlias]['CHARSET']) ) {
  380.         sq_bindtextdomain'squirrelmail'SM_PATH 'locale/' );
  381.         sq_textdomain'squirrelmail' );
  382.  
  383.         // set codeset in order to avoid gettext charset conversions
  384.         if (function_exists('bind_textdomain_codeset')) {
  385.             // Japanese translation uses different internal charset
  386.             if ($sm_notAlias == 'ja_JP'{
  387.                 bind_textdomain_codeset ('squirrelmail''EUC-JP');
  388.             else {
  389.                 bind_textdomain_codeset ('squirrelmail'$languages[$sm_notAlias]['CHARSET');
  390.             }
  391.         }
  392.  
  393.         // Use LOCALE key, if it is set.
  394.         if (isset($languages[$sm_notAlias]['LOCALE'])){
  395.             $longlocale=$languages[$sm_notAlias]['LOCALE'];
  396.         else {
  397.             $longlocale=$sm_notAlias;
  398.         }
  399.  
  400.         // try setting locale
  401.         $retlocale=sq_setlocale(LC_ALL$longlocale);
  402.  
  403.         // check if locale is set and assign that locale to $longlocale
  404.         // in order to use it in putenv calls.
  405.         if (is_bool($retlocale)) {
  406.             $longlocale=$retlocale;
  407.         elseif (is_array($longlocale)) {
  408.             // setting of all locales failed.
  409.             // we need string instead of array used in LOCALE key.
  410.             $longlocale=$sm_notAlias;
  411.         }
  412.  
  413.         if !((bool)ini_get('safe_mode')) &&
  414.              getenv'LC_ALL' != $longlocale {
  415.             putenv"LC_ALL=$longlocale);
  416.             putenv"LANG=$longlocale);
  417.             putenv"LANGUAGE=$longlocale);
  418.             putenv"LC_NUMERIC=C" );
  419.             if ($sm_notAlias=='tr_TR'putenv"LC_CTYPE=C" );
  420.         }
  421.         // Workaround for plugins that use numbers with floating point
  422.         // It might be removed if plugins use correct decimal delimiters
  423.         // according to locale settings.
  424.         setlocale(LC_NUMERIC'C');
  425.         // Workaround for specific Turkish strtolower/strtoupper rules.
  426.         // Many functions expect English conversion rules.
  427.         if ($sm_notAlias=='tr_TR'setlocale(LC_CTYPE,'C');
  428.  
  429.         /**
  430.          * Set text direction/alignment variables
  431.          * When language environment is setup, scripts can use these globals
  432.          * without accessing $languages directly and making checks for optional
  433.          * array key.
  434.          */
  435.         global $text_direction$left_align$right_align;
  436.         if (isset($languages[$sm_notAlias]['DIR']&&
  437.             $languages[$sm_notAlias]['DIR'== 'rtl'{
  438.             /**
  439.              * Text direction
  440.              * @global string $text_direction
  441.              */
  442.             $text_direction='rtl';
  443.             /**
  444.              * Left alignment
  445.              * @global string $left_align
  446.              */
  447.             $left_align='right';
  448.             /**
  449.              * Right alignment
  450.              * @global string $right_align
  451.              */
  452.             $right_align='left';
  453.         else {
  454.             $text_direction='ltr';
  455.             $left_align='left';
  456.             $right_align='right';
  457.         }
  458.  
  459.         $squirrelmail_language $sm_notAlias;
  460.         if ($squirrelmail_language == 'ja_JP'{
  461.             header ('Content-Type: text/html; charset=EUC-JP');
  462.             if (!function_exists('mb_internal_encoding')) {
  463.                 // Error messages can't be displayed here
  464.                 $error 1;
  465.                 // Revert to English if possible.
  466.                 if (function_exists('setPref')  && $username!='' && $data_dir!=""{
  467.                     setPref($data_dir$username'language'"en_US");
  468.                     $error 2;
  469.                 }
  470.                 // stop further execution in order not to get php errors on mb_internal_encoding().
  471.                 return $error;
  472.             }
  473.             if (function_exists('mb_language')) {
  474.                 mb_language('Japanese');
  475.             }
  476.             mb_internal_encoding('EUC-JP');
  477.             mb_http_output('pass');
  478.         elseif ($squirrelmail_language == 'en_US'{
  479.             header'Content-Type: text/html; charset=' $default_charset );
  480.         else {
  481.             header'Content-Type: text/html; charset=' $languages[$sm_notAlias]['CHARSET');
  482.         }
  483.         /**
  484.          * mbstring.func_overload fix (#929644).
  485.          *
  486.          * php mbstring extension can replace standard string functions with their multibyte
  487.          * equivalents. See http://www.php.net/ref.mbstring#mbstring.overload. This feature
  488.          * was added in php v.4.2.0
  489.          *
  490.          * Some SquirrelMail functions work with 8bit strings in bytes. If interface is forced
  491.          * to use mbstring functions and mbstring internal encoding is set to multibyte charset,
  492.          * interface can't trust regular string functions. Due to mbstring overloading design
  493.          * limits php scripts can't control this setting.
  494.          *
  495.          * This hack should fix some issues related to 8bit strings in passwords. Correct fix is
  496.          * to disable mbstring overloading. Japanese translation uses different internal encoding.
  497.          */
  498.         if ($squirrelmail_language != 'ja_JP' &&
  499.             function_exists('mb_internal_encoding'&&
  500.             check_php_version(4,2,0&&
  501.             (int)ini_get('mbstring.func_overload')!=0{
  502.             mb_internal_encoding('pass');
  503.         }
  504.     }
  505.     return 0;
  506. }
  507.  
  508. /**
  509.  * Sets default_charset variable according to the one that is used by user's translations.
  510.  *
  511.  * Function changes global $default_charset variable in order to be sure, that it
  512.  * contains charset used by user's translation. Sanity of $squirrelmail_language
  513.  * and $default_charset combination is also tested.
  514.  *
  515.  * There can be a $default_charset setting in the
  516.  * config.php file, but the user may have a different language
  517.  * selected for a user interface. This function checks the
  518.  * language selected by the user and tags the outgoing messages
  519.  * with the appropriate charset corresponding to the language
  520.  * selection. This is "more right" (tm), than just stamping the
  521.  * message blindly with the system-wide $default_charset.
  522.  */
  523. function set_my_charset(){
  524.     global $data_dir$username$default_charset$languages$squirrelmail_language;
  525.  
  526.     $my_language getPref($data_dir$username'language');
  527.     if (!$my_language{
  528.         $my_language $squirrelmail_language ;
  529.     }
  530.     // Catch removed translation
  531.     if (!isset($languages[$my_language])) {
  532.         $my_language="en_US";
  533.     }
  534.     while (isset($languages[$my_language]['ALIAS'])) {
  535.         $my_language $languages[$my_language]['ALIAS'];
  536.     }
  537.     $my_charset $languages[$my_language]['CHARSET'];
  538.     if ($my_language!='en_US'{
  539.         $default_charset $my_charset;
  540.     }
  541. }
  542.  
  543. /**
  544.  * Replaces non-braking spaces inserted by some browsers with regular space
  545.  *
  546.  * This function can be used to replace non-braking space symbols
  547.  * that are inserted in forms by some browsers instead of normal
  548.  * space symbol.
  549.  *
  550.  * @param string $string Text that needs to be cleaned
  551.  * @param string $charset Charset used in text
  552.  * @return string Cleaned text
  553.  */
  554. function cleanup_nbsp($string,$charset{
  555.  
  556.   // reduce number of case statements
  557.   if (stristr('iso-8859-',substr($charset,0,9))){
  558.     $output_charset="iso-8859-x";
  559.   }
  560.   if (stristr('windows-125',substr($charset,0,11))){
  561.     $output_charset="cp125x";
  562.   }
  563.   if (stristr('koi8',substr($charset,0,4))){
  564.     $output_charset="koi8-x";
  565.   }
  566.   if (isset($output_charset)){
  567.     $output_charset=strtolower($charset);
  568.   }
  569.  
  570. // where is non-braking space symbol
  571. switch($output_charset):
  572.  case "iso-8859-x":
  573.  case "cp125x":
  574.  case "iso-2022-jp":
  575.   $nbsp="\xA0";
  576.   break;
  577.  case "koi8-x":
  578.    $nbsp="\x9A";
  579.    break;
  580.  case "utf-8":
  581.    $nbsp="\xC2\xA0";
  582.    break;
  583.  default:
  584.    // don't change string if charset is unmatched
  585.    return $string;
  586. endswitch;
  587.  
  588. // return space instead of non-braking space.
  589.  return str_replace($nbsp,' ',$string);
  590. }
  591.  
  592. /**
  593.  * Function informs if it is safe to convert given charset to the one that is used by user.
  594.  *
  595.  * It is safe to use conversion only if user uses utf-8 encoding and when
  596.  * converted charset is similar to the one that is used by user.
  597.  *
  598.  * @param string $input_charset Charset of text that needs to be converted
  599.  * @return bool is it possible to convert to user's charset
  600.  */
  601. function is_conversion_safe($input_charset{
  602.     global $languages$sm_notAlias$default_charset$lossy_encoding;
  603.  
  604.     if (isset($lossy_encoding&& $lossy_encoding )
  605.         return true;
  606.  
  607.     // convert to lower case
  608.     $input_charset strtolower($input_charset);
  609.  
  610.     // Is user's locale Unicode based ?
  611.     if $default_charset == "utf-8" {
  612.         return true;
  613.     }
  614.  
  615.     // Charsets that are similar
  616.     switch ($default_charset{
  617.     case "windows-1251":
  618.         if $input_charset == "iso-8859-5" ||
  619.              $input_charset == "koi8-r" ||
  620.              $input_charset == "koi8-u" {
  621.             return true;
  622.         else {
  623.             return false;
  624.         }
  625.     case "windows-1257":
  626.         if $input_charset == "iso-8859-13" ||
  627.              $input_charset == "iso-8859-4" {
  628.             return true;
  629.         else {
  630.             return false;
  631.         }
  632.     case "iso-8859-4":
  633.         if $input_charset == "iso-8859-13" ||
  634.              $input_charset == "windows-1257" {
  635.             return true;
  636.         else {
  637.             return false;
  638.         }
  639.     case "iso-8859-5":
  640.         if $input_charset == "windows-1251" ||
  641.              $input_charset == "koi8-r" ||
  642.              $input_charset == "koi8-u" {
  643.             return true;
  644.         else {
  645.             return false;
  646.         }
  647.     case "iso-8859-13":
  648.         if $input_charset == "iso-8859-4" ||
  649.              $input_charset == "windows-1257" {
  650.             return true;
  651.         else {
  652.             return false;
  653.         }
  654.     case "koi8-r":
  655.         if $input_charset == "windows-1251" ||
  656.              $input_charset == "iso-8859-5" ||
  657.              $input_charset == "koi8-u" {
  658.             return true;
  659.         else {
  660.             return false;
  661.         }
  662.     case "koi8-u":
  663.         if $input_charset == "windows-1251" ||
  664.              $input_charset == "iso-8859-5" ||
  665.              $input_charset == "koi8-r" {
  666.             return true;
  667.         else {
  668.             return false;
  669.         }
  670.     default:
  671.         return false;
  672.     }
  673. }
  674.  
  675. /**
  676.  * Converts html character entities to numeric entities
  677.  *
  678.  * SquirrelMail encoding functions work only with numeric entities.
  679.  * This function fixes issues with decoding functions that might convert
  680.  * some symbols to character entities. Issue is specific to PHP recode
  681.  * extension decoding. Function is used internally in charset_convert()
  682.  * function.
  683.  * @param string $str string that might contain html character entities
  684.  * @return string string with character entities converted to decimals.
  685.  * @since 1.5.2
  686.  */
  687. function sqi18n_convert_entities($str{
  688.  
  689.     $entities array(
  690.         // Latin 1
  691.         '&nbsp;'   => '&#160;',
  692.         '&iexcl;'  => '&#161;',
  693.         '&cent;'   => '&#162;',
  694.         '&pound;'  => '&#163;',
  695.         '&curren;' => '&#164;',
  696.         '&yen;'    => '&#165;',
  697.         '&brvbar;' => '&#166;',
  698.         '&sect;'   => '&#167;',
  699.         '&uml;'    => '&#168;',
  700.         '&copy;'   => '&#169;',
  701.         '&ordf;'   => '&#170;',
  702.         '&laquo;'  => '&#171;',
  703.         '&not;'    => '&#172;',
  704.         '&shy;'    => '&#173;',
  705.         '&reg;'    => '&#174;',
  706.         '&macr;'   => '&#175;',
  707.         '&deg;'    => '&#176;',
  708.         '&plusmn;' => '&#177;',
  709.         '&sup2;'   => '&#178;',
  710.         '&sup3;'   => '&#179;',
  711.         '&acute;'  => '&#180;',
  712.         '&micro;'  => '&#181;',
  713.         '&para;'   => '&#182;',
  714.         '&middot;' => '&#183;',
  715.         '&cedil;'  => '&#184;',
  716.         '&sup1;'   => '&#185;',
  717.         '&ordm;'   => '&#186;',
  718.         '&raquo;'  => '&#187;',
  719.         '&frac14;' => '&#188;',
  720.         '&frac12;' => '&#189;',
  721.         '&frac34;' => '&#190;',
  722.         '&iquest;' => '&#191;',
  723.         '&Agrave;' => '&#192;',
  724.         '&Aacute;' => '&#193;',
  725.         '&Acirc;'  => '&#194;',
  726.         '&Atilde;' => '&#195;',
  727.         '&Auml;'   => '&#196;',
  728.         '&Aring;'  => '&#197;',
  729.         '&AElig;'  => '&#198;',
  730.         '&Ccedil;' => '&#199;',
  731.         '&Egrave;' => '&#200;',
  732.         '&Eacute;' => '&#201;',
  733.         '&Ecirc;'  => '&#202;',
  734.         '&Euml;'   => '&#203;',
  735.         '&Igrave;' => '&#204;',
  736.         '&Iacute;' => '&#205;',
  737.         '&Icirc;'  => '&#206;',
  738.         '&Iuml;'   => '&#207;',
  739.         '&ETH;'    => '&#208;',
  740.         '&Ntilde;' => '&#209;',
  741.         '&Ograve;' => '&#210;',
  742.         '&Oacute;' => '&#211;',
  743.         '&Ocirc;'  => '&#212;',
  744.         '&Otilde;' => '&#213;',
  745.         '&Ouml;'   => '&#214;',
  746.         '&times;'  => '&#215;',
  747.         '&Oslash;' => '&#216;',
  748.         '&Ugrave;' => '&#217;',
  749.         '&Uacute;' => '&#218;',
  750.         '&Ucirc;'  => '&#219;',
  751.         '&Uuml;'   => '&#220;',
  752.         '&Yacute;' => '&#221;',
  753.         '&THORN;'  => '&#222;',
  754.         '&szlig;'  => '&#223;',
  755.         '&agrave;' => '&#224;',
  756.         '&aacute;' => '&#225;',
  757.         '&acirc;'  => '&#226;',
  758.         '&atilde;' => '&#227;',
  759.         '&auml;'   => '&#228;',
  760.         '&aring;'  => '&#229;',
  761.         '&aelig;'  => '&#230;',
  762.         '&ccedil;' => '&#231;',
  763.         '&egrave;' => '&#232;',
  764.         '&eacute;' => '&#233;',
  765.         '&ecirc;'  => '&#234;',
  766.         '&euml;'   => '&#235;',
  767.         '&igrave;' => '&#236;',
  768.         '&iacute;' => '&#237;',
  769.         '&icirc;'  => '&#238;',
  770.         '&iuml;'   => '&#239;',
  771.         '&eth;'    => '&#240;',
  772.         '&ntilde;' => '&#241;',
  773.         '&ograve;' => '&#242;',
  774.         '&oacute;' => '&#243;',
  775.         '&ocirc;'  => '&#244;',
  776.         '&otilde;' => '&#245;',
  777.         '&ouml;'   => '&#246;',
  778.         '&divide;' => '&#247;',
  779.         '&oslash;' => '&#248;',
  780.         '&ugrave;' => '&#249;',
  781.         '&uacute;' => '&#250;',
  782.         '&ucirc;'  => '&#251;',
  783.         '&uuml;'   => '&#252;',
  784.         '&yacute;' => '&#253;',
  785.         '&thorn;'  => '&#254;',
  786.         '&yuml;'   => '&#255;',
  787.         // Latin Extended-A
  788.         '&OElig;'  => '&#338;',
  789.         '&oelig;'  => '&#339;',
  790.         '&Scaron;' => '&#352;',
  791.         '&scaron;' => '&#353;',
  792.         '&Yuml;'   => '&#376;',
  793.         // Spacing Modifier Letters
  794.         '&circ;'   => '&#710;',
  795.         '&tilde;'  => '&#732;',
  796.         // General Punctuation
  797.         '&ensp;'   => '&#8194;',
  798.         '&emsp;'   => '&#8195;',
  799.         '&thinsp;' => '&#8201;',
  800.         '&zwnj;'   => '&#8204;',
  801.         '&zwj;'    => '&#8205;',
  802.         '&lrm;'    => '&#8206;',
  803.         '&rlm;'    => '&#8207;',
  804.         '&ndash;'  => '&#8211;',
  805.         '&mdash;'  => '&#8212;',
  806.         '&lsquo;'  => '&#8216;',
  807.         '&rsquo;'  => '&#8217;',
  808.         '&sbquo;'  => '&#8218;',
  809.         '&ldquo;'  => '&#8220;',
  810.         '&rdquo;'  => '&#8221;',
  811.         '&bdquo;'  => '&#8222;',
  812.         '&dagger;' => '&#8224;',
  813.         '&Dagger;' => '&#8225;',
  814.         '&permil;' => '&#8240;',
  815.         '&lsaquo;' => '&#8249;',
  816.         '&rsaquo;' => '&#8250;',
  817.         '&euro;'   => '&#8364;',
  818.         // Latin Extended-B
  819.         '&fnof;' => '&#402;',
  820.         // Greek
  821.         '&Alpha;'  => '&#913;',
  822.         '&Beta;'   => '&#914;',
  823.         '&Gamma;'  => '&#915;',
  824.         '&Delta;'  => '&#916;',
  825.         '&Epsilon;' => '&#917;',
  826.         '&Zeta;'   => '&#918;',
  827.         '&Eta;'    => '&#919;',
  828.         '&Theta;'  => '&#920;',
  829.         '&Iota;'   => '&#921;',
  830.         '&Kappa;'  => '&#922;',
  831.         '&Lambda;' => '&#923;',
  832.         '&Mu;'     => '&#924;',
  833.         '&Nu;'     => '&#925;',
  834.         '&Xi;'     => '&#926;',
  835.         '&Omicron;' => '&#927;',
  836.         '&Pi;'     => '&#928;',
  837.         '&Rho;'    => '&#929;',
  838.         '&Sigma;'  => '&#931;',
  839.         '&Tau;'    => '&#932;',
  840.         '&Upsilon;' => '&#933;',
  841.         '&Phi;'    => '&#934;',
  842.         '&Chi;'    => '&#935;',
  843.         '&Psi;'    => '&#936;',
  844.         '&Omega;'  => '&#937;',
  845.         '&alpha;'  => '&#945;',
  846.         '&beta;'   => '&#946;',
  847.         '&gamma;'  => '&#947;',
  848.         '&delta;'  => '&#948;',
  849.         '&epsilon;' => '&#949;',
  850.         '&zeta;'   => '&#950;',
  851.         '&eta;'    => '&#951;',
  852.         '&theta;'  => '&#952;',
  853.         '&iota;'   => '&#953;',
  854.         '&kappa;'  => '&#954;',
  855.         '&lambda;' => '&#955;',
  856.         '&mu;'     => '&#956;',
  857.         '&nu;'     => '&#957;',
  858.         '&xi;'     => '&#958;',
  859.         '&omicron;' => '&#959;',
  860.         '&pi;'     => '&#960;',
  861.         '&rho;'    => '&#961;',
  862.         '&sigmaf;' => '&#962;',
  863.         '&sigma;'  => '&#963;',
  864.         '&tau;'    => '&#964;',
  865.         '&upsilon;' => '&#965;',
  866.         '&phi;'    => '&#966;',
  867.         '&chi;'    => '&#967;',
  868.         '&psi;'    => '&#968;',
  869.         '&omega;'  => '&#969;',
  870.         '&thetasym;' => '&#977;',
  871.         '&upsih;'  => '&#978;',
  872.         '&piv;'    => '&#982;',
  873.         // General Punctuation
  874.         '&bull;'   => '&#8226;',
  875.         '&hellip;' => '&#8230;',
  876.         '&prime;'  => '&#8242;',
  877.         '&Prime;'  => '&#8243;',
  878.         '&oline;'  => '&#8254;',
  879.         '&frasl;'  => '&#8260;',
  880.         // Letterlike Symbols
  881.         '&weierp;' => '&#8472;',
  882.         '&image;'  => '&#8465;',
  883.         '&real;'   => '&#8476;',
  884.         '&trade;'  => '&#8482;',
  885.         '&alefsym;' => '&#8501;',
  886.         // Arrows
  887.         '&larr;'   => '&#8592;',
  888.         '&uarr;'   => '&#8593;',
  889.         '&rarr;'   => '&#8594;',
  890.         '&darr;'   => '&#8595;',
  891.         '&harr;'   => '&#8596;',
  892.         '&crarr;'  => '&#8629;',
  893.         '&lArr;'   => '&#8656;',
  894.         '&uArr;'   => '&#8657;',
  895.         '&rArr;'   => '&#8658;',
  896.         '&dArr;'   => '&#8659;',
  897.         '&hArr;'   => '&#8660;',
  898.         // Mathematical Operators
  899.         '&forall;' => '&#8704;',
  900.         '&part;'   => '&#8706;',
  901.         '&exist;'  => '&#8707;',
  902.         '&empty;'  => '&#8709;',
  903.         '&nabla;'  => '&#8711;',
  904.         '&isin;'   => '&#8712;',
  905.         '&notin;'  => '&#8713;',
  906.         '&ni;'     => '&#8715;',
  907.         '&prod;'   => '&#8719;',
  908.         '&sum;'    => '&#8721;',
  909.         '&minus;'  => '&#8722;',
  910.         '&lowast;' => '&#8727;',
  911.         '&radic;'  => '&#8730;',
  912.         '&prop;'   => '&#8733;',
  913.         '&infin;'  => '&#8734;',
  914.         '&ang;'    => '&#8736;',
  915.         '&and;'    => '&#8743;',
  916.         '&or;'     => '&#8744;',
  917.         '&cap;'    => '&#8745;',
  918.         '&cup;'    => '&#8746;',
  919.         '&int;'    => '&#8747;',
  920.         '&there4;' => '&#8756;',
  921.         '&sim;'    => '&#8764;',
  922.         '&cong;'   => '&#8773;',
  923.         '&asymp;'  => '&#8776;',
  924.         '&ne;'     => '&#8800;',
  925.         '&equiv;'  => '&#8801;',
  926.         '&le;'     => '&#8804;',
  927.         '&ge;'     => '&#8805;',
  928.         '&sub;'    => '&#8834;',
  929.         '&sup;'    => '&#8835;',
  930.         '&nsub;'   => '&#8836;',
  931.         '&sube;'   => '&#8838;',
  932.         '&supe;'   => '&#8839;',
  933.         '&oplus;'  => '&#8853;',
  934.         '&otimes;' => '&#8855;',
  935.         '&perp;'   => '&#8869;',
  936.         '&sdot;'   => '&#8901;',
  937.         // Miscellaneous Technical
  938.         '&lceil;'  => '&#8968;',
  939.         '&rceil;'  => '&#8969;',
  940.         '&lfloor;' => '&#8970;',
  941.         '&rfloor;' => '&#8971;',
  942.         '&lang;'   => '&#9001;',
  943.         '&rang;'   => '&#9002;',
  944.         // Geometric Shapes
  945.         '&loz;'    => '&#9674;',
  946.         // Miscellaneous Symbols
  947.         '&spades;' => '&#9824;',
  948.         '&clubs;'  => '&#9827;',
  949.         '&hearts;' => '&#9829;',
  950.         '&diams;'  => '&#9830;');
  951.  
  952.     $str str_replace(array_keys($entities)array_values($entities)$str);
  953.  
  954.     return $str;
  955. }
  956.  
  957. /* ------------------------------ main --------------------------- */
  958.  
  959. global $squirrelmail_language$languages$use_gettext;
  960.  
  961. if (sqgetGlobalVar('squirrelmail_language',$squirrelmail_language,SQ_COOKIE)) {
  962.     $squirrelmail_language '';
  963. }
  964.  
  965. /**
  966.  * Array specifies the available translations.
  967.  *
  968.  * Structure of array:
  969.  * $languages['language']['variable'] = 'value'
  970.  *
  971.  * Possible 'variable' names:
  972.  *  NAME      - Translation name in English
  973.  *  CHARSET   - Encoding used by translation
  974.  *  ALIAS     - used when 'language' is only short name and 'value' should provide long language name
  975.  *  ALTNAME   - Native translation name. Any 8bit symbols must be html encoded.
  976.  *  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
  977.  *  DIR       - Text direction. Used to define Right-to-Left languages. Possible values 'rtl' or 'ltr'. If undefined - defaults to 'ltr'
  978.  *  XTRA_CODE - translation uses special functions. See doc/i18n.txt
  979.  *
  980.  * Each 'language' definition requires NAME+CHARSET or ALIAS variables.
  981.  *
  982.  * @name $languages
  983.  * @global array $languages 
  984.  */
  985. $languages['en_US']['NAME']    'English';
  986. $languages['en_US']['CHARSET''iso-8859-1';
  987. $languages['en_US']['LOCALE']  'en_US.ISO8859-1';
  988. $languages['en']['ALIAS''en_US';
  989.  
  990. /**
  991.  * Automatic translation loading from setup.php files.
  992.  * Solution for bug. 1240889.
  993.  * setup.php file can contain $languages array entries and XTRA_CODE functions.
  994.  */
  995. if (is_dir(SM_PATH 'locale'&&
  996.     is_readable(SM_PATH 'locale')) {
  997.     $localedir dir(SM_PATH 'locale');
  998.     while($lang_dir=$localedir->read()) {
  999.         // remove trailing slash, if present
  1000.                 if (substr($lang_dir,-1)=='/'{
  1001.             $lang_dir substr($lang_dir,0,-1);
  1002.         }
  1003.         if ($lang_dir != '..' && $lang_dir != '.' && $lang_dir != 'CVS' &&
  1004.             is_dir(SM_PATH.'locale/'.$lang_dir&&
  1005.             file_exists(SM_PATH.'locale/'.$lang_dir.'/setup.php')) {
  1006.             include_once(SM_PATH.'locale/'.$lang_dir.'/setup.php');
  1007.         }
  1008.     }
  1009.     $localedir->close();
  1010. }
  1011.  
  1012. /* Detect whether gettext is installed. */
  1013. $gettext_flags 0;
  1014. if (function_exists('_')) {
  1015.     $gettext_flags += 1;
  1016. }
  1017. if (function_exists('bindtextdomain')) {
  1018.     $gettext_flags += 2;
  1019. }
  1020. if (function_exists('textdomain')) {
  1021.     $gettext_flags += 4;
  1022. }
  1023. if (function_exists('ngettext')) {
  1024.     $gettext_flags += 8;
  1025. }
  1026.  
  1027. /* If gettext is fully loaded, cool */
  1028. if ($gettext_flags == 15{
  1029.     $use_gettext true;
  1030. }
  1031.  
  1032. /* If ngettext support is missing, load it */
  1033. elseif ($gettext_flags == 7{
  1034.     $use_gettext true;
  1035.     // load internal ngettext functions
  1036.         include_once(SM_PATH 'class/l10n.class.php');
  1037.     include_once(SM_PATH 'functions/ngettext.php');
  1038. }
  1039.  
  1040. /* If we can fake gettext, try that */
  1041. elseif ($gettext_flags == 0{
  1042.     $use_gettext true;
  1043.     include_once(SM_PATH 'functions/gettext.php');
  1044. else {
  1045.     /* Uh-ho.  A weird install */
  1046.     if ($gettext_flags 1{
  1047.       /**
  1048.        * Function is used as replacement in broken installs
  1049.        * @ignore
  1050.        */
  1051.         function _($str{
  1052.             return $str;
  1053.         }
  1054.     }
  1055.     if ($gettext_flags 2{
  1056.       /**
  1057.        * Function is used as replacement in broken installs
  1058.        * @ignore
  1059.        */
  1060.         function bindtextdomain({
  1061.             return;
  1062.         }
  1063.     }
  1064.     if ($gettext_flags 4{
  1065.       /**
  1066.        * Function is used as replacemet in broken installs
  1067.        * @ignore
  1068.        */
  1069.         function textdomain({
  1070.             return;
  1071.         }
  1072.     }
  1073.     if ($gettext_flags 8{
  1074.         /**
  1075.          * Function is used as replacemet in broken installs
  1076.          * @ignore
  1077.          */
  1078.         function ngettext($str,$str2,$number{
  1079.             if ($number>1{
  1080.                 return $str2;
  1081.             else {
  1082.                 return $str;
  1083.             }
  1084.         }
  1085.     }
  1086.     if (function_exists('dgettext')) {
  1087.         /**
  1088.          * Replacement for broken setups.
  1089.          * @ignore
  1090.          */
  1091.         function dgettext($domain,$str{
  1092.             return $str;
  1093.         }
  1094.     }
  1095.     if (function_exists('dngettext')) {
  1096.         /**
  1097.          * Replacement for broken setups
  1098.          * @ignore
  1099.          */
  1100.         function dngettext($domain,$str1,$strn,$number{
  1101.             return ($number==$str1 $strn);
  1102.         }
  1103.     }
  1104. }

Documentation generated on Sat, 07 Oct 2006 16:12:05 +0300 by phpDocumentor 1.3.0RC6