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

Documentation generated on Mon, 13 Jan 2020 04:22:55 +0100 by phpDocumentor 1.4.3