]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/Localization/Localization.php
Release 6.5.0
[Github/sugarcrm.git] / include / Localization / Localization.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /**
39  * Localization manager
40  * @api
41  */
42 class Localization {
43         var $availableCharsets = array(
44                 'BIG-5',        //Taiwan and Hong Kong
45                 /*'CP866'                         // ms-dos Cyrillic */
46                 /*'CP949'                         //Microsoft Korean */
47                 'CP1251',       //MS Cyrillic
48                 'CP1252',       //MS Western European & US
49                 'EUC-CN',       //Simplified Chinese GB2312
50                 'EUC-JP',       //Unix Japanese
51                 'EUC-KR',       //Korean
52                 'EUC-TW',       //Taiwanese
53                 'ISO-2022-JP',  //Japanese
54                 'ISO-2022-KR',  //Korean
55                 'ISO-8859-1',   //Western European and US
56                 'ISO-8859-2',   //Central and Eastern European
57                 'ISO-8859-3',   //Latin 3
58                 'ISO-8859-4',   //Latin 4
59                 'ISO-8859-5',   //Cyrillic
60                 'ISO-8859-6',   //Arabic
61                 'ISO-8859-7',   //Greek
62                 'ISO-8859-8',   //Hebrew
63                 'ISO-8859-9',   //Latin 5
64                 'ISO-8859-10',  //Latin 6
65                 'ISO-8859-13',  //Latin 7
66                 'ISO-8859-14',  //Latin 8
67                 'ISO-8859-15',  //Latin 9
68                 'KOI8-R',       //Cyrillic Russian
69                 'KOI8-U',       //Cyrillic Ukranian
70                 'SJIS',         //MS Japanese
71                 'UTF-8',        //UTF-8
72                 );
73         var $localeNameFormat;
74         var $localeNameFormatDefault;
75         var $default_export_charset = 'UTF-8';
76         var $default_email_charset = 'UTF-8';
77         var $currencies = array(); // array loaded with current currencies
78     var $invalidNameFormatUpgradeFilename = 'upgradeInvalidLocaleNameFormat.php';
79     /* Charset mappings for iconv */
80     var $iconvCharsetMap = array(
81         'KS_C_5601-1987' => 'CP949',
82         'ISO-8859-8-I' => 'ISO-8859-8'            
83         );
84
85         /**
86          * sole constructor
87          */
88         function Localization() {
89                 global $sugar_config;
90                 $this->localeNameFormatDefault = empty($sugar_config['locale_name_format_default']) ? 's f l' : $sugar_config['default_name_format'];
91                 $this->loadCurrencies();
92         }
93
94         /**
95          * returns an array of Sugar Config defaults that are determined by locale settings
96          * @return array
97          */
98         function getLocaleConfigDefaults() {
99                 $coreDefaults = array(
100                         'currency'                                                              => '',
101                         'datef'                                                                 => 'm/d/Y',
102                         'timef'                                                                 => 'H:i',
103                         'default_currency_significant_digits'   => 2,
104                         'default_currency_symbol'                               => '$',
105                         'default_export_charset'                                => $this->default_export_charset,
106                         'default_locale_name_format'                    => 's f l',
107             'name_formats'                          => array('s f l' => 's f l', 'f l' => 'f l', 's l' => 's l', 'l, s f' => 'l, s f',
108                                                             'l, f' => 'l, f', 's l, f' => 's l, f', 'l s f' => 'l s f', 'l f s' => 'l f s'),
109                         'default_number_grouping_seperator'             => ',',
110                         'default_decimal_seperator'                             => '.',
111                         'export_delimiter'                                              => ',',
112                         'default_email_charset'                                 => $this->default_email_charset,
113                 );
114
115                 return $coreDefaults;
116         }
117
118         /**
119          * abstraction of precedence
120          * @param string prefName Name of preference to retrieve based on overrides
121          * @param object user User in focus, default null (current_user)
122          * @return string pref Most significant preference
123          */
124         function getPrecedentPreference($prefName, $user=null, $sugarConfigPrefName = '') {
125                 global $current_user;
126                 global $sugar_config;
127
128                 $userPref = '';
129                 $coreDefaults = $this->getLocaleConfigDefaults();
130                 $pref = isset($coreDefaults[$prefName]) ? $coreDefaults[$prefName] : ''; // defaults, even before config.php
131
132                 if($user != null) {
133                         $userPref = $user->getPreference($prefName);
134                 } elseif(!empty($current_user)) {
135                         $userPref = $current_user->getPreference($prefName);
136                 }
137                 // Bug 39171 - If we are asking for default_email_charset, check in emailSettings['defaultOutboundCharset'] as well
138                 if ( $prefName == 'default_email_charset' ) {
139                     if($user != null) {
140                 $emailSettings = $user->getPreference('emailSettings', 'Emails');
141             } elseif(!empty($current_user)) {
142                 $emailSettings = $current_user->getPreference('emailSettings', 'Emails');
143             }
144             if ( isset($emailSettings['defaultOutboundCharset']) ) {
145                 $userPref = $emailSettings['defaultOutboundCharset'];
146             }
147                 }
148
149                 // set fallback defaults defined in this class
150                 if(isset($this->$prefName)) {
151                         $pref = $this->$prefName;
152                 }
153                 //rrs: 33086 - give the ability to pass in the preference name as stored in $sugar_config.
154                 if(!empty($sugarConfigPrefName)){
155                         $prefName = $sugarConfigPrefName;
156                 }
157                 // cn: 9549 empty() call on a value of 0 (0 significant digits) resulted in a false-positive.  changing to "isset()"
158                 $pref = (!isset($sugar_config[$prefName]) || (empty($sugar_config[$prefName]) && $sugar_config[$prefName] !== '0')) ? $pref : $sugar_config[$prefName];
159                 $pref = (empty($userPref) && $userPref !== '0') ? $pref : $userPref;
160                 return $pref;
161         }
162
163         ///////////////////////////////////////////////////////////////////////////
164         ////    CURRENCY HANDLING
165         /**
166          * wrapper for whatever currency system we implement
167          */
168         function loadCurrencies() {
169                 // doing it dirty here
170                 global $db;
171                 global $sugar_config;
172
173                 if(empty($db)) {
174                         return array();
175                 }
176
177         $load = sugar_cache_retrieve('currency_list');
178         if ( !is_array($load) ) {
179                         // load default from config.php
180                         $this->currencies['-99'] = array(
181                                 'name'          => $sugar_config['default_currency_name'],
182                                 'symbol'        => $sugar_config['default_currency_symbol'],
183                                 'conversion_rate' => 1
184                                 );
185
186             $q = "SELECT id, name, symbol, conversion_rate FROM currencies WHERE status = 'Active' and deleted = 0";
187             $r = $db->query($q);
188
189             while($a = $db->fetchByAssoc($r)) {
190                 $load = array();
191                 $load['name'] = $a['name'];
192                 $load['symbol'] = $a['symbol'];
193                 $load['conversion_rate'] = $a['conversion_rate'];
194
195                 $this->currencies[$a['id']] = $load;
196             }
197             sugar_cache_put('currency_list',$this->currencies);
198         } else {
199             $this->currencies = $load;
200         }
201
202
203         }
204
205         /**
206          * getter for currencies array
207          * @return array $this->currencies returns array( id => array(name => X, etc
208          */
209         function getCurrencies() {
210                 return $this->currencies;
211         }
212
213         /**
214          * retrieves default OOTB currencies for sugar_config and installer.
215          * @return array ret Array of default currencies keyed by ISO4217 code
216          */
217         function getDefaultCurrencies() {
218                 $ret = array(
219                         'AUD' => array( 'name'          => 'Australian Dollars',
220                                                         'iso4217'       => 'AUD',
221                                                         'symbol'        => '$'),
222                         'BRL' => array( 'name'          => 'Brazilian Reais',
223                                                         'iso4217'       => 'BRL',
224                                                         'symbol'        => 'R$'),
225                         'GBP' => array( 'name'          => 'British Pounds',
226                                                         'iso4217'       => 'GBP',
227                                                         'symbol'        => '£'),
228                         'CAD' => array( 'name'          => 'Canadian Dollars',
229                                                         'iso4217'       => 'CAD',
230                                                         'symbol'        => '$'),
231                         'CNY' => array( 'name'          => 'Chinese Yuan',
232                                                         'iso4217'       => 'CNY',
233                                                         'symbol'        => 'ï¿¥'),
234                         'EUR' => array( 'name'          => 'Euro',
235                                                         'iso4217'       => 'EUR',
236                                                         'symbol'        => '€'),
237                         'HKD' => array( 'name'          => 'Hong Kong Dollars',
238                                                         'iso4217'       => 'HKD',
239                                                         'symbol'        => '$'),
240                         'INR' => array( 'name'          => 'Indian Rupees',
241                                                         'iso4217'       => 'INR',
242                                                         'symbol'        => '₨'),
243                         'KRW' => array( 'name'          => 'Korean Won',
244                                                         'iso4217'       => 'KRW',
245                                                         'symbol'        => 'â‚©'),
246                         'YEN' => array( 'name'          => 'Japanese Yen',
247                                                         'iso4217'       => 'JPY',
248                                                         'symbol'        => 'Â¥'),
249                         'MXM' => array( 'name'          => 'Mexican Pesos',
250                                                         'iso4217'       => 'MXM',
251                                                         'symbol'        => '$'),
252                         'SGD' => array( 'name'          => 'Singaporean Dollars',
253                                                         'iso4217'       => 'SGD',
254                                                         'symbol'        => '$'),
255                         'CHF' => array( 'name'          => 'Swiss Franc',
256                                                         'iso4217'       => 'CHF',
257                                                         'symbol'        => 'SFr.'),
258                         'THB' => array( 'name'          => 'Thai Baht',
259                                                         'iso4217'       => 'THB',
260                                                         'symbol'        => '฿'),
261                         'USD' => array( 'name'          => 'US Dollars',
262                                                         'iso4217'       => 'USD',
263                                                         'symbol'        => '$'),
264                 );
265
266                 return $ret;
267         }
268         ////    END CURRENCY HANDLING
269         ///////////////////////////////////////////////////////////////////////////
270
271
272         ///////////////////////////////////////////////////////////////////////////
273         ////    CHARSET TRANSLATION
274         /**
275          * returns a mod|app_strings array in the target charset
276          * @param array strings $mod_string, et.al.
277          * @param string charset Target charset
278          * @return array Translated string pack
279          */
280         function translateStringPack($strings, $charset) {
281                 // handle recursive
282                 foreach($strings as $k => $v) {
283                         if(is_array($v)) {
284                                 $strings[$k] = $this->translateStringPack($v, $charset);
285                         } else {
286                                 $strings[$k] = $this->translateCharset($v, 'UTF-8', $charset);
287                         }
288                 }
289                 ksort($strings);
290                 return $strings;
291         }
292
293         /**
294          * translates the passed variable for email sending (export)
295          * @param       mixed the var (array or string) to translate
296          * @return      mixed the translated variable
297          */
298         function translateForEmail($var) {
299                 if(is_array($var)) {
300                         foreach($var as $k => $v) {
301                                 $var[$k] = $this->translateForEmail($v);
302                         }
303                         return $var;
304                 } elseif(!empty($var)) {
305                         return $this->translateCharset($var, 'UTF-8', $this->getOutboundEmailCharset());
306                 }
307         }
308
309         /**
310          * prepares a bean for export by translating any text fields into the export
311          * character set
312          * @param bean object A SugarBean
313          * @return bean object The bean with translated strings
314          */
315     function prepBeanForExport($bean)
316     {
317         foreach($bean->field_defs as $k => $field)
318         {
319             if (is_string($bean->$k))
320             {
321                            // $bean->$k = $this->translateCharset($bean->$k, 'UTF-8', $this->getExportCharset());
322             }
323             else
324             {
325                 $bean->$k = '';
326             }
327         }
328
329         return $bean;
330     }
331
332         /**
333          * translates a character set from one encoding to another encoding
334          * @param string string the string to be translated
335          * @param string fromCharset the charset the string is currently in
336          * @param string toCharset the charset to translate into (defaults to UTF-8)
337          * @param bool   forceIconv force using the iconv library instead of mb_string
338          * @return string the translated string
339          */
340     function translateCharset($string, $fromCharset, $toCharset='UTF-8', $forceIconv = false)
341     {
342         $GLOBALS['log']->debug("Localization: translating [{$string}] from {$fromCharset} into {$toCharset}");
343
344         // Bug #35413 Function has to use iconv if $fromCharset is not in mb_list_encodings
345         $isMb = function_exists('mb_convert_encoding') && !$forceIconv;
346         $isIconv = function_exists('iconv');
347         if ($isMb == true)
348         {
349             $fromCharset = strtoupper($fromCharset);
350             $listEncodings = mb_list_encodings();
351             $isFound = false;
352             foreach ($listEncodings as $encoding)
353             {
354                 if (strtoupper($encoding) == $fromCharset)
355                 {
356                     $isFound = true;
357                     break;
358                 }
359             }
360             $isMb = $isFound;
361         }
362
363         if($isMb)
364         {
365             return mb_convert_encoding($string, $toCharset, $fromCharset);
366         }
367         elseif($isIconv)
368         {
369             $newFromCharset = $fromCharset;
370             if (isset($this->iconvCharsetMap[$fromCharset])) {
371                 $newFromCharset = $this->iconvCharsetMap[$fromCharset];
372                 $GLOBALS['log']->debug("Localization: iconv using charset {$newFromCharset} instead of {$fromCharset}");
373             }
374             $newToCharset = $toCharset;
375             if (isset($this->iconvCharsetMap[$toCharset])) {
376                 $newToCharset = $this->iconvCharsetMap[$toCharset];
377                 $GLOBALS['log']->debug("Localization: iconv using charset {$newToCharset} instead of {$toCharset}");
378             }
379             return iconv($newFromCharset, $newToCharset, $string);
380         }
381         else
382         {
383             return $string;
384         } // end else clause
385     }
386
387         /**
388          * translates a character set from one to another, and the into MIME-header friendly format
389          */
390         function translateCharsetMIME($string, $fromCharset, $toCharset='UTF-8', $encoding="Q") {
391                 $previousEncoding = mb_internal_encoding();
392             mb_internal_encoding($toCharset);
393                 $result = mb_encode_mimeheader($string, $toCharset, $encoding);
394                 mb_internal_encoding($previousEncoding);
395                 return $result;
396         }
397
398         function normalizeCharset($charset) {
399                 $charset = strtolower(preg_replace("/[\-\_]*/", "", $charset));
400                 return $charset;
401         }
402
403         /**
404          * returns an array of charsets with keys for available translations; appropriate for get_select_options_with_id()
405          */
406         function getCharsetSelect() {
407     //jc:12293 - the "labels" or "human-readable" representations of the various charsets
408     //should be translatable
409     $translated = array();
410     foreach($this->availableCharsets as $key)
411     {
412          //$translated[$key] = translate($value);
413          $translated[$key] = translate($key);
414     }
415
416                 return $translated;
417     //end:12293
418         }
419
420         /**
421          * returns the charset preferred in descending order: User, Sugar Config, DEFAULT
422          * @param string charset to override ALL, pass a valid charset here
423          * @return string charset the chosen character set
424          */
425         function getExportCharset($charset='', $user=null) {
426                 $charset = $this->getPrecedentPreference('default_export_charset', $user);
427                 return $charset;
428         }
429
430         /**
431          * returns the charset preferred in descending order: User, Sugar Config, DEFAULT
432          * @return string charset the chosen character set
433          */
434         function getOutboundEmailCharset($user=null) {
435                 $charset = $this->getPrecedentPreference('default_email_charset', $user);
436                 return $charset;
437         }
438         ////    END CHARSET TRANSLATION
439         ///////////////////////////////////////////////////////////////////////////
440
441         ///////////////////////////////////////////////////////////////////////////
442         ////    NUMBER DISPLAY FORMATTING CODE
443         function getDecimalSeparator($user=null) {
444         // Bug50887 this is purposefully misspelled as ..._seperator to match the way it's defined throughout the app.
445                 $dec = $this->getPrecedentPreference('default_decimal_seperator', $user);
446                 return $dec;
447         }
448
449         function getNumberGroupingSeparator($user=null) {
450                 $sep = $this->getPrecedentPreference('default_number_grouping_seperator', $user);
451                 return $sep;
452         }
453
454         function getPrecision($user=null) {
455                 $precision = $this->getPrecedentPreference('default_currency_significant_digits', $user);
456                 return $precision;
457         }
458
459         function getCurrencySymbol($user=null) {
460                 $dec = $this->getPrecedentPreference('default_currency_symbol', $user);
461                 return $dec;
462         }
463
464         /**
465          * returns a number formatted by user preference or system default
466          * @param string number Number to be formatted and returned
467          * @param string currencySymbol Currency symbol if override is necessary
468          * @param bool is_currency Flag to also return the currency symbol
469          * @return string Formatted number
470          */
471         function getLocaleFormattedNumber($number, $currencySymbol='', $is_currency=true, $user=null) {
472                 $fnum                   = $number;
473                 $majorDigits    = '';
474                 $minorDigits    = '';
475                 $dec                    = $this->getDecimalSeparator($user);
476                 $thou                   = $this->getNumberGroupingSeparator($user);
477                 $precision              = $this->getPrecision($user);
478                 $symbol                 = empty($currencySymbol) ? $this->getCurrencySymbol($user) : $currencySymbol;
479
480                 $exNum = explode($dec, $number);
481                 // handle grouping
482                 if(is_array($exNum) && count($exNum) > 0) {
483                         if(strlen($exNum[0]) > 3) {
484                                 $offset = strlen($exNum[0]) % 3;
485                                 if($offset > 0) {
486                                         for($i=0; $i<$offset; $i++) {
487                                                 $majorDigits .= $exNum[0]{$i};
488                                         }
489                                 }
490
491                                 $tic = 0;
492                                 for($i=$offset; $i<strlen($exNum[0]); $i++) {
493                                         if($tic % 3 == 0 && $i != 0) {
494                                                 $majorDigits .= $thou; // add separator
495                                         }
496
497                                         $majorDigits .= $exNum[0]{$i};
498                                         $tic++;
499                                 }
500                         } else {
501                                 $majorDigits = $exNum[0]; // no formatting needed
502                         }
503                         $fnum = $majorDigits;
504                 }
505
506                 // handle decimals
507                 if($precision > 0) { // we toss the minor digits otherwise
508                         if(is_array($exNum) && isset($exNum[1])) {
509
510                         }
511                 }
512
513
514                 if($is_currency) {
515                         $fnum = $symbol.$fnum;
516                 }
517                 return $fnum;
518         }
519
520         /**
521          * returns Javascript to format numbers and currency for ***DISPLAY***
522          */
523         function getNumberJs() {
524                 $out = <<<eoq
525
526                         var exampleDigits = '123456789.000000';
527
528                         // round parameter can be negative for decimal, precision has to be postive
529                         function formatNumber(n, sep, dec, precision) {
530                                 var majorDigits;
531                                 var minorDigits;
532                                 var formattedMajor = '';
533                                 var formattedMinor = '';
534
535                                 var nArray = n.split('.');
536                                 majorDigits = nArray[0];
537                                 if(nArray.length < 2) {
538                                         minorDigits = 0;
539                                 } else {
540                                         minorDigits = nArray[1];
541                                 }
542
543                                 // handle grouping
544                                 if(sep.length > 0) {
545                                         var strlength = majorDigits.length;
546
547                                         if(strlength > 3) {
548                                                 var offset = strlength % 3; // find how many to lead off by
549
550                                                 for(j=0; j<offset; j++) {
551                                                         formattedMajor += majorDigits[j];
552                                                 }
553
554                                                 tic=0;
555                                                 for(i=offset; i<strlength; i++) {
556                                                         if(tic % 3 == 0 && i != 0)
557                                                                 formattedMajor += sep;
558
559                                                         formattedMajor += majorDigits.substr(i,1);
560                                                         tic++;
561                                                 }
562                                         }
563                                 } else {
564                                         formattedMajor = majorDigits; // no grouping marker
565                                 }
566
567                                 // handle decimal precision
568                                 if(precision > 0) {
569                                         for(i=0; i<precision; i++) {
570                                                 if(minorDigits[i] != undefined)
571                                                         formattedMinor += minorDigits[i];
572                                                 else
573                                                         formattedMinor += '0';
574                                         }
575                                 } else {
576                                         // we're just returning the major digits, no decimal marker
577                                         dec = ''; // just in case
578                                 }
579
580                                 return formattedMajor + dec + formattedMinor;
581                         }
582
583                         function setSigDigits() {
584                                 var sym = document.getElementById('symbol').value;
585                                 var thou = document.getElementById('default_number_grouping_seperator').value;
586                                 var dec = document.getElementById('default_decimal_seperator').value;
587                                 var precision = document.getElementById('sigDigits').value;
588                                 //umber(n, num_grp_sep, dec_sep, round, precision)
589                                 var newNumber = sym + formatNumber(exampleDigits, thou, dec, precision, precision);
590                                 document.getElementById('sigDigitsExample').value = newNumber;
591                         }
592 eoq;
593                 return $out;
594         }
595
596         ////    END NUMBER DISPLAY FORMATTING CODE
597         ///////////////////////////////////////////////////////////////////////////
598
599         ///////////////////////////////////////////////////////////////////////////
600         ////    NAME DISPLAY FORMATTING CODE
601         /**
602          * get's the Name format macro string, preferring $current_user
603          * @return string format Name Format macro for locale
604          */
605         function getLocaleFormatMacro($user=null) {
606                 $returnFormat = $this->getPrecedentPreference('default_locale_name_format', $user);
607                 return $returnFormat;
608         }
609
610         /**
611          * returns formatted name according to $current_user's locale settings
612          *
613          * @param string firstName
614          * @param string lastName
615          * @param string salutation
616          * @param string title
617          * @param string format If a particular format is desired, then pass this optional parameter as a simple string.
618          * sfl is "Salutation FirstName LastName", "l, f s" is "LastName[comma][space]FirstName[space]Salutation"
619          * @param object user object
620          * @param bool returnEmptyStringIfEmpty true if we should return back an empty string rather than a single space
621          * when the formatted name would be blank
622          * @return string formattedName
623          */
624         function getLocaleFormattedName($firstName, $lastName, $salutationKey='', $title='', $format="", $user=null, $returnEmptyStringIfEmpty = false) {
625                 global $current_user;
626                 global $app_list_strings;
627
628                 if ( $user == null ) {
629                     $user = $current_user;
630                 }
631
632                 $salutation = $salutationKey;
633                 if(!empty($salutationKey) && !empty($app_list_strings['salutation_dom'][$salutationKey])) {
634                         $salutation = (!empty($app_list_strings['salutation_dom'][$salutationKey]) ? $app_list_strings['salutation_dom'][$salutationKey] : $salutationKey);
635                 }
636
637         //check to see if passed in variables are set, if so, then populate array with value,
638         //if not, then populate array with blank ''
639                 $names = array();
640                 $names['f'] = (empty($firstName)        && $firstName   != 0) ? '' : $firstName;
641                 $names['l'] = (empty($lastName) && $lastName    != 0) ? '' : $lastName;
642                 $names['s'] = (empty($salutation)       && $salutation  != 0) ? '' : $salutation;
643                 $names['t'] = (empty($title)            && $title               != 0) ? '' : $title;
644
645                 //Bug: 39936 - if all of the inputs are empty, then don't try to format the name.
646                 $allEmpty = true;
647                 foreach($names as $key => $val){
648                         if(!empty($val)){
649                                 $allEmpty = false;
650                                 break;
651                         }
652                 }
653                 if($allEmpty){
654                         return $returnEmptyStringIfEmpty ? '' : ' ';
655                 }
656                 //end Bug: 39936
657
658                 if(empty($format)) {
659                         $this->localeNameFormat = $this->getLocaleFormatMacro($user);
660                 } else {
661                         $this->localeNameFormat = $format;
662                 }
663
664                 // parse localeNameFormat
665                 $formattedName = '';
666                 for($i=0; $i<strlen($this->localeNameFormat); $i++) {
667                         $formattedName .= array_key_exists($this->localeNameFormat{$i}, $names) ? $names[$this->localeNameFormat{$i}] : $this->localeNameFormat{$i};
668                 }
669
670                 $formattedName = trim($formattedName);
671         if (strlen($formattedName)==0) {
672             return $returnEmptyStringIfEmpty ? '' : ' ';
673         }
674
675                 if(strpos($formattedName,',',strlen($formattedName)-1)) { // remove trailing commas
676                         $formattedName = substr($formattedName, 0, strlen($formattedName)-1);
677                 }
678                 return trim($formattedName);
679         }
680
681         /**
682          * outputs some simple Javascript to show a preview of Name format in "My Account" and "Admin->Localization"
683          * @param string first First Name, use app_strings default if not specified
684          * @param string last Last Name, use app_strings default if not specified
685          * @param string salutation Saluation, use app_strings default if not specified
686          * @return string some Javascript
687          */
688         function getNameJs($first='', $last='', $salutation='', $title='') {
689                 global $app_strings;
690
691                 $salutation     = !empty($salutation) ? $salutation : $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'];
692                 $first          = !empty($first) ? $first : $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'];
693                 $last           = !empty($last) ? $last : $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST'];
694                 $title          = !empty($title) ? $title : $app_strings['LBL_LOCALE_NAME_EXAMPLE_TITLE'];
695
696                 $ret = "
697                 function setPreview() {
698                         format = document.getElementById('default_locale_name_format').value;
699                         field = document.getElementById('nameTarget');
700
701                         stuff = new Object();
702
703                         stuff['s'] = '{$salutation}';
704                         stuff['f'] = '{$first}';
705                         stuff['l'] = '{$last}';
706                         stuff['t'] = '{$title}';
707
708                         var name = '';
709                         for(i=0; i<format.length; i++) {
710                 if(stuff[format.substr(i,1)] != undefined) {
711                     name += stuff[format.substr(i,1)];
712                                 } else {
713                     name += format.substr(i,1);
714                 }
715                         }
716
717                         //alert(name);
718                         field.value = name;
719                 }
720
721         ";
722
723                 return $ret;
724         }
725
726     /**
727      * Checks to see that the characters in $name_format are allowed:  s, f, l, space/tab or punctuation
728      * @param $name_format
729      * @return bool
730      */
731     public function isAllowedNameFormat($name_format) {
732         // will result in a match as soon as a disallowed char is hit in $name_format
733         $match = preg_match('/[^sfl[:punct:][:^alnum:]\s]/', $name_format);
734         if ($match !== false && $match === 0) {
735             return true;
736         }
737         return false;
738     }
739
740     /**
741      * Checks to see if there was an invalid Name Format encountered during the upgrade
742      * @return bool true if there was an invalid name, false if all went well.
743      */
744     public function invalidLocaleNameFormatUpgrade() {
745         return file_exists($this->invalidNameFormatUpgradeFilename);
746     }
747
748     /**
749      * Creates the file that is created when there is an invalid name format during an upgrade
750      */
751     public function createInvalidLocaleNameFormatUpgradeNotice() {
752         $fh = fopen($this->invalidNameFormatUpgradeFilename,'w');
753         fclose($fh);
754     }
755
756     /**
757      * Removes the file that is created when there is an invalid name format during an upgrade
758      */
759     public function removeInvalidLocaleNameFormatUpgradeNotice() {
760         if ($this->invalidLocaleNameFormatUpgrade()) {
761             unlink($this->invalidNameFormatUpgradeFilename);
762         }
763     }
764
765
766     /**
767      * Creates dropdown items that have localized example names while filtering out invalid formats
768      *
769      * @param array un-prettied dropdown list
770      * @return array array of dropdown options
771      */
772     public function getUsableLocaleNameOptions($options) {
773         global $app_strings;
774
775         $examples = array('s' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'],
776                         'f' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'],
777                         'l' => $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST']);
778         $newOpts = array();
779         foreach ($options as $key => $val) {
780             if ($this->isAllowedNameFormat($key) && $this->isAllowedNameFormat($val)) {
781                 $newVal = '';
782                 $pieces = str_split($val);
783                 foreach ($pieces as $piece) {
784                     if (isset($examples[$piece])) {
785                         $newVal .= $examples[$piece];
786                     } else {
787                         $newVal .= $piece;
788                     }
789                 }
790                 $newOpts[$key] = $newVal;
791             }
792         }
793         return $newOpts;
794     }
795         ////    END NAME DISPLAY FORMATTING CODE
796         ///////////////////////////////////////////////////////////////////////////
797
798     /**
799      * Attempts to detect the charset used in the string
800      *
801      * @param  $str string
802      * @param $strict bool default false (use strict encoding?)
803      * @return string
804      */
805     public function detectCharset($str, $strict=false)
806     {
807         if ( function_exists('mb_convert_encoding') )
808             return mb_detect_encoding($str,'ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-8859-1',$strict);
809
810         return false;
811     }
812 } // end class def
813
814 ?>