Internationalisation and my PHP Development Infrastructure
By Tony Marston2005-07-17
My Implementation - Handling Numbers
Although the English notation for numbers is to use '.' (period) for the decimal separator and ',' (comma) for the thousands separator there are some countries which use a different notation. Some have the two separators completely reversed, and some use ' ' (space) as the thousands separator. Regardless of any national conventions all numbers are processed within the program code, and stored within the database, in a common format. That is, the decimal point is a '.' (period) and there are no thousands separators.
This means that all decimal values must be formatted before they can be output to the user, and any user input must be unformatted before it can be handled by the program.
Convert to external (user's) format
A very important step in this process is therefore to identify all the decimal format conventions expected by the user. Fortunately all the relevant information can be provided by the localeconv() function. Unfortunately this requires the user's actual locale to be identified first with the setlocale() function. I say 'unfortunately' because the input to this function is the user's current locale or location whereas the only information available at present is the user's preferred language as supplied in the HTTP variables. I have got round this minor annoyance by modifying the 'languages' array used to determine the user's language to include a locale in the full language string, as in the following examples:
- English (United Kingdom) [ENG]
- English (United States) [USA]
- French (Canada) [CAN]
- French (France) [FRA]
- German (Germany) [DEU]
- Spanish (Spain) [ESP]
This means that I can now set the user's locale using code similar to the following:
// get full language string from first entry in user_language_array
$country = $_SESSION['user_language_array'][0][2];
// extract locale which is enclosed in '[' and ']'
if (!preg_match('?\[[^\[]+\]?', $country, $regs)) {
// 'Locale is not defined in string'
trigger_error(getLanguageText('sys0078', $country), E_USER_ERROR);
} // if
$locale = trim($regs[0], '[]');
// find out if this is a valid locale
if (!$locale = setLocale(LC_ALL, $locale)) {
// 'Cannot set locale'
trigger_error(getLanguageText('sys0079', $locale), E_USER_ERROR);
} // if
Having set the locale it is then a relatively simple exercise to convert any decimal number from internal to external format using code similar to the following:
$decimal_places = $this->fieldspec[$fieldname]['scale'];
$locale = localeconv();
$decimal_point = $locale['decimal_point'];
$thousands_sep = $locale['thousands_sep'];
if ($thousands_sep == chr(160)) {
// change non-breaking space into ordinary space
$thousands_sep = chr(32);
} // if
$fieldvalue = number_format($fieldvalue,
$decimal_places,
$decimal_point,
$thousands_sep);
Convert to internal format
When the user presses the SUBMIT button any numbers that have been input will need to be converted back into internal format before they can be processed. This is done with code similar to the following:
function number_unformat ($input)
// convert input string into a number using settings from localeconv()
{
$locale = localeconv();
$decimal_point = $locale['decimal_point'];
$thousands_sep = $locale['thousands_sep'];
if ($thousands_sep == chr(160)) {
// change non-breaking space into ordinary space
$thousands_sep = chr(32);
} // if
$count = count_chars($input, 1);
if ($count[ord($decimal_point)] > 1) {
// too many decimal places
return $input;
} // if
// split number into 2 distinct parts
list($integer, $fraction) = explode($decimal_point, $input);
// remove thousands separator
$integer = str_replace($thousands_sep, NULL, $integer);
// join the two parts back together again
$number = $integer .'.' .$fraction;
return $number;
} // number_unformat
Tutorial Pages:
» Introduction
» Possible Methods
» Design Decisions
» My Implementation - Directory Structure
» My Implementation - File Names
» My Implementation - Determine User Language
» My Implementation - Locate Language Subdirectory
» My Implementation - Load Screen Structure file
» My Implementation - Get Language Text
» My Implementation - Get Language Array
» My Implementation - Handling Dates
» My Implementation - Handling Numbers
» Conclusion
» References
