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