title besides title

 

Tuesday, November 27, 2012

PHP : Internationalization and Localization - [16.7] Localizing Currency Values

16.7.1 Problem

You want to display currency amounts in a locale-specific format.

16.7.2 Solution

Use the pc_format_currency( ) function, shown in Example 16-1, to produce an appropriately formatted string. For example:
setlocale(LC_ALL,'fr_CA');
print pc_format_currency(-12345678.45);
(12 345 678,45 $)

16.7.3 Discussion

The pc_format_currency( ) function, shown in Example 16-1, gets the currency formatting information from localeconv( ) and then uses number_format( ) and some logic to construct the correct string.
Example 16-1. pc_format_currency
function pc_format_currency($amt) {
    // get locale-specific currency formatting information 
    $a = localeconv();
    
    // compute sign of $amt and then remove it
    if ($amt < 0) { $sign = -1; } else { $sign = 1; }
    $amt = abs($amt);
    // format $amt with appropriate grouping, decimal point, and fractional digits 
    $amt = number_format($amt,$a['frac_digits'],$a['mon_decimal_point'],
                         $a['mon_thousands_sep']);
    
    // figure out where to put the currency symbol and positive or negative signs
    $currency_symbol = $a['currency_symbol'];
    // is $amt >= 0 ? 
    if (1 == $sign) {
        $sign_symbol  = 'positive_sign';
        $cs_precedes  = 'p_cs_precedes';
        $sign_posn    = 'p_sign_posn';
        $sep_by_space = 'p_sep_by_space';
    } else {
        $sign_symbol  = 'negative_sign';
        $cs_precedes  = 'n_cs_precedes';
        $sign_posn    = 'n_sign_posn';
        $sep_by_space = 'n_sep_by_space';
    }
    if ($a[$cs_precedes]) {
        if (3 == $a[$sign_posn]) {
            $currency_symbol = $a[$sign_symbol].$currency_symbol;
        } elseif (4 == $a[$sign_posn]) {
            $currency_symbol .= $a[$sign_symbol];
        }
        // currency symbol in front 
        if ($a[$sep_by_space]) {
            $amt = $currency_symbol.' '.$amt;
        } else {
            $amt = $currency_symbol.$amt;
        }
    } else {
        // currency symbol after amount 
        if ($a[$sep_by_space]) {
            $amt .= ' '.$currency_symbol;
        } else {
            $amt .= $currency_symbol;
        }
    }
    if (0 == $a[$sign_posn]) {
        $amt = "($amt)";
    } elseif (1 == $a[$sign_posn]) {
        $amt = $a[$sign_symbol].$amt;
    } elseif (2 == $a[$sign_posn]) {
        $amt .= $a[$sign_symbol];
    }
    return $amt;
The code in pc_format_currency( ) that puts the currency symbol and sign in the correct place is almost identical for positive and negative amounts; it just uses different elements of the array returned by localeconv( ). The relevant elements of localeconv( )'s returned array are shown in Table 16-1.

Table 16-1. Currency-related information from localeconv( )
Array element
Description
currency_symbol
Local currency symbol
mon_decimal_point
Monetary decimal point character
mon_thousands_sep
Monetary thousands separator
positive_sign
Sign for positive values
negative_sign
Sign for negative values
frac_digits
Number of fractional digits
p_cs_precedes
1 if currency_symbol should precede a positive value, 0 if it should follow
p_sep_by_space
1 if a space should separate the currency symbol from a positive value, 0 if not
n_cs_precedes
1 if currency_symbol should precede a negative value, 0 if it should follow
n_sep_by_space
1 if a space should separate currency_symbol from a negative value, 0 if not
p_sign_posn
Positive sign position:
  • 0if parenthesis should surround the quantity and currency_symbol
  • 1 if the sign string should precede the quantity and currency_symbol
  • 2 if the sign string should follow the quantity and currency_symbol
  • 3 if the sign string should immediately precede currency_symbol
  • 4 if the sign string should immediately follow currency_symbol
n_sign_posn
Negative sign position: same possible values as p_sign_posn
There is a function in the C library called strfmon( ) that does for currency what strftime( ) does for dates and times; however, it isn't implemented in PHP. The pc_format_currency( ) function provides most of the same capabilities.