1<?php
2/*
3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004-2021 The Cacti Group                                 |
5 |                                                                         |
6 | This program is free software; you can redistribute it and/or           |
7 | modify it under the terms of the GNU General Public License             |
8 | as published by the Free Software Foundation; either version 2          |
9 | of the License, or (at your option) any later version.                  |
10 |                                                                         |
11 | This program is distributed in the hope that it will be useful,         |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
14 | GNU General Public License for more details.                            |
15 +-------------------------------------------------------------------------+
16 | Cacti: The Complete RRDtool-based Graphing Solution                     |
17 +-------------------------------------------------------------------------+
18 | This code is designed, written, and maintained by the Cacti Group. See  |
19 | about.php and/or the AUTHORS file for specific developer information.   |
20 +-------------------------------------------------------------------------+
21 | http://www.cacti.net/                                                   |
22 +-------------------------------------------------------------------------+
23*/
24
25/* default localization of Cacti */
26$cacti_locale  = 'en-US';
27$cacti_country = 'us';
28
29/* an array that will contains all textdomains being in use. */
30$cacti_textdomains = array();
31
32global $path2calendar, $path2timepicker, $path2colorpicker;
33
34/* get a list of locale settings */
35$lang2locale = get_list_of_locales();
36
37/* use a fallback if i18n is disabled (default) */
38if (!read_config_option('i18n_language_support') && read_config_option('i18n_language_support') != '') {
39	l10n_debug("load_fallback_procedure(1)");
40	load_fallback_procedure();
41	return;
42}
43
44/* Repair legacy language support */
45if (isset($_REQUEST['language'])) {
46	$_REQUEST['language'] = repair_locale($_REQUEST['language']);
47}
48
49/* determine whether or not we can support the language */
50$user_locale = '';
51if (isset($_REQUEST['language']) && isset($lang2locale[$_REQUEST['language']])) {
52	/* user requests another language */
53	$user_locale = apply_locale($_REQUEST['language']);
54	unset($_SESSION['sess_current_date1']);
55	unset($_SESSION['sess_current_date2']);
56
57	/* save customized language setting (authenticated users only) */
58	set_user_setting('language', $user_locale);
59} elseif (isset($_SESSION['sess_user_language']) && isset($lang2locale[$_SESSION['sess_user_language']])) {
60	/* language definition stored in the SESSION */
61	$user_locale = apply_locale($_SESSION['sess_user_language']);
62} else {
63	/* look up for user customized language setting stored in Cacti DB */
64	$user_locale = apply_locale(read_user_i18n_setting('user_language'));
65}
66
67/* allow RRDtool to display i18n */
68setlocale(LC_CTYPE, str_replace('-', '_', $user_locale) . '.UTF-8');
69
70if ($user_locale !== false && $user_locale !== '') {
71	$_SESSION['sess_user_language'] = $user_locale;
72}
73
74/* define the path to the language file */
75l10n_debug("search(1): " . $config['base_path'] . '/locales/LC_MESSAGES/' . $cacti_locale . '.mo');
76l10n_debug("search(2): " . $config['base_path'] . '/locales/LC_MESSAGES/' . $lang2locale[$cacti_locale]['filename'] . '.mo');
77
78if (file_exists($config['base_path'] . '/locales/LC_MESSAGES/' . $cacti_locale . '.mo')) {
79	$path2catalogue = $config['base_path'] . '/locales/LC_MESSAGES/' . $cacti_locale . '.mo';
80} elseif (file_exists($config['base_path'] . '/locales/LC_MESSAGES/' . $lang2locale[$cacti_locale]['filename'] . '.mo')) {
81	$path2catalogue = $config['base_path'] . '/locales/LC_MESSAGES/' . $lang2locale[$cacti_locale]['filename'] . '.mo';
82} else {
83	$path2catalogue = '';
84}
85
86$catalogue = $path2catalogue;
87
88/* define the path to the language file of the DHTML calendar */
89if ($cacti_locale != '') {
90	$lang_parts = explode('-', $cacti_locale);
91
92	// Detect the calendar path
93	if (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery-ui-datepicker-' . $cacti_locale . '.js')) {
94		$path2calendar = $config['include_path'] . '/js/LC_MESSAGES/jquery-ui-datepicker-' . $cacti_locale . '.js';
95	} elseif (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery-ui-datepicker-' . $lang_parts[0] . '.js')) {
96		$path2calendar = $config['include_path'] . '/js/LC_MESSAGES/jquery-ui-datepicker-' . $lang_parts[0] . '.js';
97	} else {
98		$path2calendar = '';
99	}
100
101	// Detect the timepicker path
102	if (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery-ui-timepicker-' . $cacti_locale . '.js')) {
103		$path2timepicker = $config['include_path'] . '/js/LC_MESSAGES/jquery-ui-timepicker-' . $cacti_locale . '.js';
104	} elseif (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery-ui-timepicker-' . $lang_parts[0] . '.js')) {
105		$path2timepicker = $config['include_path'] . '/js/LC_MESSAGES/jquery-ui-timepicker-' . $lang_parts[0] . '.js';
106	} else {
107		$path2timepicker = '';
108	}
109
110	// Detect the colorpicker path
111	if (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery.ui.colorpicker-' . $cacti_locale . '.js')) {
112		$path2colorpicker = $config['include_path'] . '/js/LC_MESSAGES/jquery.ui.colorpicker-' . $cacti_locale . '.js';
113	} elseif (file_exists($config['include_path'] . '/js/LC_MESSAGES/jquery.ui.colorpicker-' . $lang_parts[0] . '.js')) {
114		$path2colorpicker = $config['include_path'] . '/js/LC_MESSAGES/jquery.ui.colorpicker-' . $lang_parts[0] . '.js';
115	} else {
116		$path2colorpicker = '';
117	}
118} else {
119	$path2timepicker  = '';
120	$path2calendar    = '';
121	$path2colorpicker = '';
122}
123
124/* use fallback procedure if requested language is not available */
125if (file_exists($path2catalogue)) {
126	$cacti_textdomains['cacti']['path2catalogue'] = $path2catalogue;
127} else {
128	l10n_debug("load_fallback_procedure(2): " . $path2catalogue);
129	load_fallback_procedure();
130	return;
131}
132
133/* search the correct textdomains for all plugins being installed */
134$plugins = db_fetch_assoc('SELECT `directory`
135	FROM `plugin_config`
136	ORDER BY id');
137
138if ($plugins && cacti_sizeof($plugins)) {
139	foreach ($plugins as $plugin) {
140		$plugin = $plugin['directory'];
141
142		if (file_exists($config['base_path'] . '/plugins/' . $plugin . '/locales/LC_MESSAGES/' . $cacti_locale . '.mo')) {
143			$path2catalogue = $config['base_path'] . '/plugins/' . $plugin . '/locales/LC_MESSAGES/' . $cacti_locale . '.mo';
144		} elseif (file_exists($config['base_path'] . '/plugins/' . $plugin . '/locales/LC_MESSAGES/' . $lang2locale[$cacti_locale]['filename'] . '.mo')) {
145			$path2catalogue = $config['base_path'] . '/plugins/' . $plugin . '/locales/LC_MESSAGES/' . $lang2locale[$cacti_locale]['filename'] . '.mo';
146		}
147
148		if (file_exists($path2catalogue)) {
149			$cacti_textdomains[$plugin]['path2catalogue'] = $path2catalogue;
150		}
151	}
152
153	/* if i18n support is set to strict mode then check if all plugins support the requested language */
154	if (read_config_option('i18n_language_support') == 2) {
155		if (cacti_sizeof($plugins) != (cacti_sizeof($cacti_textdomains) - 1)) {
156			l10n_debug("load_fallback_procedure(3)");
157			load_fallback_procedure();
158			return;
159		}
160	}
161}
162
163l10n_debug("require(1)");
164
165/* load php-gettext class if present */
166$l10n = array();
167
168// Is the handler defined in the db?
169$l10n_handler = read_config_option('l10n_language_handler');
170
171// Is the handler defined in the config but not the db?
172if (empty($l10n_handler) && !empty($config['l10n_language_handler'])) {
173	l10n_debug('Handler: not specified in settings');
174	$l10n_handler = $config['l10n_language_handler'];
175}
176
177if (empty($l10n_handler)) {
178	l10n_debug('Handler: not specified in config, autodetection is now in progress');
179	if (file_exists($config['include_path'] . '/vendor/gettext/src/Translator.php')) {
180		$l10n_handler = CACTI_LANGUAGE_HANDLER_OSCAROTERO;
181	} elseif (file_exists($config['include_path'] . '/vendor/phpgettext/streams.php')) {
182		$l10n_handler = CACTI_LANGUAGE_HANDLER_PHPGETTEXT;
183	} elseif (file_exists($config['include_path'] . '/vendor/motranslator/src/Translator.php')) {
184		$l10n_handler = CACTI_LANGUAGE_HANDLER_MOTRANSLATOR;
185	}
186}
187
188l10n_debug("require(1): Handler $l10n_handler");
189switch ($l10n_handler) {
190	case CACTI_LANGUAGE_HANDLER_OSCAROTERO:
191		//require($config['include_path'] . '/vendor/gettext/oscarotero.php');
192		require_once($config['include_path'] . '/vendor/gettext/src/autoloader.php');
193		require_once($config['include_path'] . '/vendor/cldr-to-gettext-plural-rules/src/autoloader.php');
194		break;
195
196	case CACTI_LANGUAGE_HANDLER_PHPGETTEXT:
197		require_once($config['include_path'] . '/vendor/phpgettext/streams.php');
198		require_once($config['include_path'] . '/vendor/phpgettext/gettext.php');
199		break;
200
201	case CACTI_LANGUAGE_HANDLER_MOTRANSLATOR:
202		require_once($config['include_path'] . '/vendor/motranslator/src/Translator.php');
203		require_once($config['include_path'] . '/vendor/motranslator/src/StringReader.php');
204		break;
205
206	default:
207		$l10n_handler = CACTI_LANGUAGE_HANDLER_NONE;
208		break;
209}
210
211define('CACTI_LANGUAGE_HANDLER', $l10n_handler);
212l10n_debug("require(2): Handler " . CACTI_LANGUAGE_HANDLER);
213
214if (CACTI_LANGUAGE_HANDLER != CACTI_LANGUAGE_HANDLER_NONE) {
215	/* prefetch all language files to work in memory only,
216	   die if one of the language files is corrupted */
217
218	foreach ($cacti_textdomains as $domain => $paths) {
219		l10n_debug("load_language($domain): " .$cacti_textdomains[$domain]['path2catalogue']);
220		switch (CACTI_LANGUAGE_HANDLER) {
221			case CACTI_LANGUAGE_HANDLER_PHPGETTEXT:
222				$l10n[$domain] = load_gettext_original($domain);
223				break;
224
225			case CACTI_LANGUAGE_HANDLER_MOTRANSLATOR:
226				$l10n[$domain] = load_gettext_motranslator($domain);
227				break;
228
229			case CACTI_LANGUAGE_HANDLER_OSCAROTERO:
230				$l10n[$domain] = load_gettext_oscarotero($domain);
231				break;
232		}
233
234		if (empty($l10n[$domain])) {
235			die('Invalid language support or corrupt/missing file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
236		}
237	}
238	unset($input);
239}
240
241/* load standard wrappers */
242define('CACTI_LOCALE', $cacti_locale);
243define('CACTI_COUNTRY', $cacti_country);
244define('CACTI_LANGUAGE', $lang2locale[CACTI_LOCALE]['language']);
245define('CACTI_LANGUAGE_FILE', $catalogue);
246
247function load_gettext_original($domain) {
248	global $cacti_textdomains;
249
250	l10n_debug("load_gettext_original($domain): " .$cacti_textdomains[$domain]['path2catalogue']);
251	$input = new FileReader($cacti_textdomains[$domain]['path2catalogue']);
252	if ($input == false) {
253		die('Unable to read file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
254	}
255
256	$l10n_domain = new gettext_reader($input);
257	if ($l10n_domain == false) {
258		die('Invalid language file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
259	}
260	return $l10n_domain;
261}
262
263function load_gettext_motranslator($domain) {
264	global $cacti_textdomains;
265
266	l10n_debug("load_gettext_mostranslator($domain): " .$cacti_textdomains[$domain]['path2catalogue']);
267	$input = new PhpMyAdmin\MoTranslator\Translator($cacti_textdomains[$domain]['path2catalogue']);
268	if ($input == false) {
269		die('Unable to read file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
270	}
271	return $input;
272}
273
274function load_gettext_oscarotero($domain) {
275	global $cacti_textdomains;
276
277	l10n_debug("load_gettext_oscarotero($domain): " .$cacti_textdomains[$domain]['path2catalogue']);
278	$input = Gettext\Translations::fromMoFile($cacti_textdomains[$domain]['path2catalogue']);
279	if ($input == false) {
280		die('Unable to read file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
281	}
282
283	$l10n_domain = new Gettext\Translator();
284	$l10n_domain->loadTranslations($input);
285	if ($l10n_domain == false) {
286		die('Invalid language file: ' . $cacti_textdomains[$domain]['path2catalogue'] . PHP_EOF);
287	}
288	return $l10n_domain;
289}
290
291function apply_locale($language) {
292	global $cacti_locale, $cacti_country, $lang2locale;
293
294	$locale_set = false;
295	if ($language != '') {
296		$language = repair_locale($language);
297		$locale_set = isset($lang2locale[$language]);
298	}
299
300	// If the users has not elected a language and autodetect is on
301	if (!$locale_set && (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && (read_config_option('i18n_auto_detection') == '' || read_config_option('i18n_auto_detection') == '1'))) {
302		/* detect browser settings if auto detection is enabled */
303		$accepted = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
304		$accepted = $accepted[0];
305
306		$language = repair_locale($accepted);
307		$locale_set = isset($lang2locale[$language]);
308	}
309
310	if (!$locale_set) {
311		$language = repair_locale(read_config_option('i18n_default_language'));
312		if ($language == false || $language == '') {
313			$language = repair_locale(read_default_config_option('i18n_default_language'));
314		}
315
316		$locale_set = isset($lang2locale[$language]);
317	}
318
319	if ($locale_set) {
320		$cacti_locale     = $language;
321		$cacti_country    = $lang2locale[$cacti_locale]['country'];
322		return $cacti_locale;
323	}
324
325	return false;
326}
327
328/* best effort function to repair locale */
329function repair_locale($language) {
330	global $lang2locale;
331
332	/* Repair legacy language support */
333	$found_locale = '';
334	$locale = str_replace('_','-', $language);
335	if (array_key_exists($locale, $lang2locale)) {
336		$language = $locale;
337	} else {
338		$wanted_locale = substr($language, 0, 2);
339		$language = '';
340		foreach ($lang2locale as $locale => $data) {
341			if (substr($locale, 0, 2) == $wanted_locale) {
342				$language = $locale;
343				break;
344			}
345		}
346	}
347
348	return $language;
349}
350
351/**
352 * Universal escaping wrappers
353 */
354function __esc() {
355	return htmlspecialchars( call_user_func_array('__', func_get_args()), ENT_QUOTES);
356}
357
358function __esc_n() {
359	return htmlspecialchars( call_user_func_array('__n', func_get_args()), ENT_QUOTES);
360}
361
362function __esc_x() {
363	return htmlspecialchars( call_user_func_array('__x', func_get_args()), ENT_QUOTES);
364}
365
366function __esc_xn() {
367	return htmlspecialchars( call_user_func_array('__xn', func_get_args()), ENT_QUOTES);
368}
369
370/**
371 * load_fallback_procedure - loads wrapper package if native language (English) has to be used
372 *
373 * @return
374 */
375function load_fallback_procedure(){
376	global $cacti_textdomains, $cacti_locale, $cacti_country, $lang2locale;
377
378	/* reset variables */
379	$_SESSION['sess_user_language'] = '';
380
381	$cacti_textdomains = array();
382	define('CACTI_LOCALE', 'en-US');
383	define('CACTI_COUNTRY', 'us');
384	define('CACTI_LANGUAGE', 'English');
385	define('CACTI_LANGUAGE_FILE', 'english_usa');
386	define('CACTI_LANGUAGE_HANDLER', CACTI_LANGUAGE_HANDLER_NONE);
387}
388
389function __gettext($text, $domain = 'cacti') {
390	global $l10n;
391
392	// Assume translation fails or is not defined
393	if (isset($l10n[$domain])) {
394		switch (CACTI_LANGUAGE_HANDLER) {
395			case CACTI_LANGUAGE_HANDLER_PHPGETTEXT:
396				$translated = $l10n[$domain]->translate($text);
397				break;
398
399			case CACTI_LANGUAGE_HANDLER_OSCAROTERO:
400			case CACTI_LANGUAGE_HANDLER_MOTRANSLATOR:
401				$translated = $l10n[$domain]->gettext($text);
402				break;
403		}
404	}
405
406	if (!isset($translated)) {
407		$translated = $text;
408	} else {
409		l10n_debug("__gettext($domain):\n	Original: $text\n	Translated: $translated", FILE_APPEND);
410	}
411
412	return __uf($translated);
413}
414
415function __n($singular, $plural, $number, $domain = 'cacti') {
416	global $l10n;
417	if (isset($l10n[$domain])) {
418		return __uf($l10n[$domain]->ngettext($singular, $plural, $number));
419	} else {
420		return ($number == 1) ? __uf($singular) : __uf($plural);
421	}
422}
423
424function __uf($text) {
425	return str_replace('%%', '%', $text);
426}
427
428function __() {
429	global $l10n;
430
431	$args = func_get_args();
432	$num  = func_num_args();
433
434	/* this should not happen */
435	if ($num < 1) {
436		return false;
437
438	/* convert pure text strings */
439	} elseif ($num == 1) {
440		return __gettext($args[0]);
441
442	/* convert pure text strings by using a different textdomain */
443	} elseif ($num == 2 && isset($l10n[$args[1]]) && $args[1] != 'cacti') {
444		return __gettext($args[0], $args[1]);
445
446	/* convert stings including one or more placeholders */
447	} else {
448		/* only the last argument is allowed to initiate
449		   the use of a different textdomain */
450
451		/* get gettext string */
452		if (isset($l10n[$args[$num-1]]) && $args[$num-1] != 'cacti') {
453			$args[0] = __gettext($args[0], $args[$num-1]);
454		} else {
455			$args[0] = __gettext($args[0]);
456		}
457
458		/* process return string against input arguments */
459		return __uf(call_user_func_array('sprintf', $args));
460	}
461}
462
463function __xn($context, $singular, $plural, $number, $domain = 'cacti') {
464	$xsingular = $context . chr(4) . $singular;
465	$xplural = $context . chr(4) . $plural;
466
467	$msgstr = __n($xsingular, $xplural, $number, $domain);
468
469	if ($number == 1 ) {
470		return ( $msgstr == $xsingular ) ? __uf($singular) : __uf($msgstr);
471	} else {
472		return ( $msgstr == $xplural ) ? __uf($plural) : __uf($msgstr);
473	}
474}
475
476function __x() {
477	global $l10n;
478
479	$args = func_get_args();
480	$num  = func_num_args();
481
482	/* this should never happen */
483	if ($num < 2) {
484		return false;
485	} else {
486		$context = array_shift($args);
487		$num--;
488
489		$msgid = reset($args);
490		$xmsgid = $context . chr(4) . $msgid;
491
492		$args[0] = $xmsgid;
493
494		if ($num == 1) {
495			/* pure text string without placeholders and a change of the default textdomain */
496			$msgstr = __gettext($args[0]);
497		} else {
498			/* get gettext string */
499			$msgstr = isset($l10n[$args[$num-1]]) && $args[$num-1] != 'cacti' ?
500			__gettext($args[0], $args[$num-1]) : __gettext($args[0]);
501		}
502
503		/* use the raw message id if language catalogue does not contain a context specific message string */
504		$args[0] = ( $msgstr == $xmsgid ) ? $msgid : $msgstr;
505
506		/* process return string against input arguments */
507		return __uf(call_user_func_array('sprintf', $args));
508	}
509}
510
511function __date($format, $timestamp = false, $domain = 'cacti') {
512	global $i18n_date_placeholders;
513
514	if (!$timestamp) {
515		$timestamp = time();
516	}
517
518	/* placeholders will allow to fill in the translated weekdays, month and so on.. */
519	$i18n_date_placeholders = array(
520		'#1' => __(date('D', $timestamp), $domain),
521		'#2' => __(date('M', $timestamp), $domain),
522		'#3' => __(date('F', $timestamp), $domain),
523		'#4' => __(date('l', $timestamp), $domain)
524	);
525
526	/* if defined exchange the format string for the configured locale */
527	$format = __gettext($format, $domain);
528
529	/* replace special date chars by placeholders */
530	$format = str_replace(array('D', 'M', 'F', 'l'), array('#1', '#2', '#3', '#4'), $format);
531
532	/* get date string included placeholders */
533	$date = date($format, $timestamp);
534
535	/* fill in specific translations */
536	$date = str_replace(array_keys($i18n_date_placeholders), array_values($i18n_date_placeholders), $date);
537
538	return __uf($date);
539}
540
541/**
542 * get_list_of_locales - returns the default settings being used for l10n
543 *
544 * @return - a multi-dimensional array with the locale code as main key
545 */
546function get_list_of_locales() {
547	$lang2locale = array(
548		'sq-AL' => array('language' => 'Albanian',            'direction' => 'ltr', 'country' => 'al', 'filename' => 'albanian_albania'),
549		'ar-SA' => array('language' => 'Arabic',              'direction' => 'rtl', 'country' => 'sa', 'filename' => 'arabic_saudi_arabia'),
550		'hy-AM' => array('language' => 'Armenian',            'direction' => 'ltr', 'country' => 'am', 'filename' => 'armenian_armenia'),
551		'be-BY' => array('language' => 'Belarusian',          'direction' => 'ltr', 'country' => 'by', 'filename' => 'belarusian_belarus'),
552		'bg-BG' => array('language' => 'Bulgarian',           'direction' => 'ltr', 'country' => 'bg', 'filename' => 'bulgarian_bulgaria'),
553		'zh-CN' => array('language' => 'Chinese (China)',     'direction' => 'ltr', 'country' => 'cn', 'filename' => 'chinese_china_simplified'),
554		'zh-HK' => array('language' => 'Chinese (Hong Kong)', 'direction' => 'ltr', 'country' => 'hk', 'filename' => 'chinese_hong_kong'),
555		'zh-SG' => array('language' => 'Chinese (Singapore)', 'direction' => 'ltr', 'country' => 'sg', 'filename' => 'chinese_singapore'),
556		'zh-TW' => array('language' => 'Chinese (Taiwan)',    'direction' => 'ltr', 'country' => 'tw', 'filename' => 'chinese_taiwan'),
557		'hr-HR' => array('language' => 'Croatian',            'direction' => 'ltr', 'country' => 'hr', 'filename' => 'croatian_croatia'),
558		'cs-GZ' => array('language' => 'Czech',               'direction' => 'ltr', 'country' => 'cz', 'filename' => 'czech_czech_republic'),
559		'da-DK' => array('language' => 'Danish',              'direction' => 'ltr', 'country' => 'dk', 'filename' => 'danish_denmark'),
560		'nl-NL' => array('language' => 'Dutch',               'direction' => 'ltr', 'country' => 'nl', 'filename' => 'dutch_netherlands'),
561		'en-US' => array('language' => 'English',             'direction' => 'ltr', 'country' => 'us', 'filename' => 'english_usa'),
562		'en-GB' => array('language' => 'English (Britain)',   'direction' => 'ltr', 'country' => 'gb', 'filename' => 'english_gb'),
563		'et-EE' => array('language' => 'Estonian',            'direction' => 'ltr', 'country' => 'ee', 'filename' => 'estonian_estonia'),
564		'fi-FI' => array('language' => 'Finnish',             'direction' => 'ltr', 'country' => 'fi', 'filename' => 'finnish_finland'),
565		'fr-FR' => array('language' => 'French',              'direction' => 'ltr', 'country' => 'fr', 'filename' => 'french_france'),
566		'de-DE' => array('language' => 'German',              'direction' => 'ltr', 'country' => 'de', 'filename' => 'german_germany'),
567		'el-GR' => array('language' => 'Greek',               'direction' => 'ltr', 'country' => 'gr', 'filename' => 'greek_greece'),
568		'he-IL' => array('language' => 'Hebrew',              'direction' => 'rtl', 'country' => 'il', 'filename' => 'hebrew_israel'),
569		'hi-IN' => array('language' => 'Hindi',               'direction' => 'ltr', 'country' => 'in', 'filename' => 'hindi_india'),
570		'hu-HU' => array('language' => 'Hungarian',           'direction' => 'ltr', 'country' => 'hu', 'filename' => 'hungarian_hungary'),
571		'is-IS' => array('language' => 'Icelandic',           'direction' => 'ltr', 'country' => 'is', 'filename' => 'icelandic_iceland'),
572		'id-ID' => array('language' => 'Indonesian',          'direction' => 'ltr', 'country' => 'id', 'filename' => 'indonesian_indonesia'),
573		'ga-IE' => array('language' => 'Irish',               'direction' => 'ltr', 'country' => 'ie', 'filename' => 'irish_ireland'),
574		'it-IT' => array('language' => 'Italian',             'direction' => 'ltr', 'country' => 'it', 'filename' => 'italian_italy'),
575		'ja-JP' => array('language' => 'Japanese',            'direction' => 'ltr', 'country' => 'jp', 'filename' => 'japanese_japan'),
576		'ko-KR' => array('language' => 'Korean',              'direction' => 'ltr', 'country' => 'kr', 'filename' => 'korean_korea'),
577		'lv-LV' => array('language' => 'Lativan',             'direction' => 'ltr', 'country' => 'lv', 'filename' => 'latvian_latvia'),
578		'lt-LT' => array('language' => 'Lithuanian',          'direction' => 'ltr', 'country' => 'lt', 'filename' => 'lithuanian_lithuania'),
579		'mk-MK' => array('language' => 'Macedonian',          'direction' => 'ltr', 'country' => 'mk', 'filename' => 'macedonian_macedonia'),
580		'ms-MY' => array('language' => 'Malay',               'direction' => 'ltr', 'country' => 'my', 'filename' => 'malay_malaysia'),
581		'mt-LT' => array('language' => 'Maltese',             'direction' => 'ltr', 'country' => 'lt', 'filename' => 'maltese_malta'),
582		'no-NO' => array('language' => 'Norwegian',           'direction' => 'ltr', 'country' => 'no', 'filename' => 'norwegian_norway'),
583		'pl-PL' => array('language' => 'Polish',              'direction' => 'ltr', 'country' => 'pl', 'filename' => 'polish_poland'),
584		'pt-PT' => array('language' => 'Portuguese',          'direction' => 'ltr', 'country' => 'pt', 'filename' => 'portuguese_portugal'),
585		'pt-BR' => array('language' => 'Portuguese (Brazil)', 'direction' => 'ltr', 'country' => 'br', 'filename' => 'portuguese_brazil'),
586		'ro-RO' => array('language' => 'Romanian',            'direction' => 'ltr', 'country' => 'ro', 'filename' => 'romanian_romania'),
587		'ru-RU' => array('language' => 'Russian',             'direction' => 'ltr', 'country' => 'ru', 'filename' => 'russian_russia'),
588		'sr-RS' => array('language' => 'Serbian',             'direction' => 'ltr', 'country' => 'rs', 'filename' => 'serbian_serbia'),
589		'sk-SK' => array('language' => 'Slovak',              'direction' => 'ltr', 'country' => 'sk', 'filename' => 'slovak_slovakia'),
590		'sl-SI' => array('language' => 'Slovenian',           'direction' => 'ltr', 'country' => 'si', 'filename' => 'slovenian_slovenia'),
591		'es-ES' => array('language' => 'Spanish',             'direction' => 'ltr', 'country' => 'es', 'filename' => 'spanish_spain'),
592		'sv-SE' => array('language' => 'Swedish',             'direction' => 'ltr', 'country' => 'se', 'filename' => 'swedish_sweden'),
593		'th-TH' => array('language' => 'Thai',                'direction' => 'ltr', 'country' => 'th', 'filename' => 'thai_thailand'),
594		'tr-TR' => array('language' => 'Turkish',             'direction' => 'ltr', 'country' => 'tr', 'filename' => 'turkish_turkey'),
595		'vi-VN' => array('language' => 'Vietnamese',          'direction' => 'ltr', 'country' => 'vn', 'filename' => 'vietnamese_vietnam')
596	);
597
598	return $lang2locale;
599}
600
601/**
602 * get_installed_locales - finds all installed locales
603 *
604 * @return - an associative array of all installed locales (e.g. 'en' => 'English')
605 */
606function get_installed_locales() {
607	global $config, $lang2locale;
608
609	$locations = array();
610	$supported_languages['en-US'] = $lang2locale['en-US']['language'];
611	foreach ($lang2locale as $locale => $properties) {
612		$locations[$properties['filename'] . '.mo'] = array(
613			'locale'   => $locale,
614			'language' => $properties['language']
615		);
616		$locations[$locale . '.mo'] = array(
617			'locale'   => $locale,
618			'language' => $properties['language']
619		);
620	}
621
622	/* create a list of all languages this Cacti system supports ... */
623	$dhandle = opendir($config['base_path'] . '/locales/LC_MESSAGES');
624	if (is_resource($dhandle)) {
625		while (false !== ($filename = readdir($dhandle))) {
626			if (isset($locations[$filename]['language'])) {
627				$supported_languages[$locations[$filename]['locale']] = $locations[$filename]['language'];
628			}
629		}
630	}
631
632	asort($supported_languages);
633
634	return $supported_languages;
635}
636
637/* read_user_i18n_setting - finds the current value of a i18n configuration setting
638   @arg $config_name - the name of the configuration setting as specified $settings_user array
639     in 'include/global_settings.php'
640   @returns - the current value of the i18n configuration option or the system default value */
641function read_user_i18n_setting($config_name) {
642	global $config;
643
644	/* users must have cacti user auth turned on to use this, or the guest account must be active */
645	if (isset($_SESSION['sess_user_id'])) {
646		$effective_uid = $_SESSION['sess_user_id'];
647	} elseif ((read_config_option('auth_method') == 0)) {
648		if (isset($_SESSION['sess_config_array'])) {
649			$config_array = $_SESSION['sess_config_array'];
650		} elseif (isset($config['config_options_array'])) {
651			$config_array = $config['config_options_array'];
652		}
653
654		if (!isset($config_array[$config_name])) {
655			$effective_uid = db_fetch_cell_prepared('SELECT id
656				FROM user_auth
657				WHERE id = ?',
658				array(get_guest_account()));
659		}
660
661		if ($effective_uid == '') {
662			$effective_uid = 0;
663		}
664	} else {
665		$effective_uid = 0;
666	}
667
668	if (db_table_exists('settings_user')) {
669		$db_setting = db_fetch_row_prepared('SELECT value
670			FROM settings_user
671			WHERE name = ?
672			AND user_id = ?',
673			array($config_name, $effective_uid));
674	}
675
676	if (isset($db_setting['value'])) {
677		return $db_setting['value'];
678	} else {
679		return false;
680	}
681}
682
683/**
684 * number_format_i18n - local specific number format wrapper
685 *
686 * @return - formatted numer in the correct locale
687 */
688function number_format_i18n($number, $decimals = null, $baseu = 1024) {
689	global $cacti_locale, $cacti_country;
690
691	$country = strtoupper($cacti_country);
692
693	if (function_exists('numfmt_create')) {
694		$fmt_key = $cacti_locale . '_'. $country;
695		$fmt = numfmt_create($fmt_key, NumberFormatter::DECIMAL);
696		if ($fmt !== false && $fmt !== null) {
697			numfmt_set_attribute($fmt, NumberFormatter::MAX_FRACTION_DIGITS, $decimals);
698
699			return numfmt_format($fmt, $number);
700		}
701		cacti_log('DEBUG: Number format \'' . $fmt_key .'\' was unavailable, using older methods',false,'i18n',POLLER_VERBOSITY_HIGH);
702	}
703
704	$origlocales = explode(';', setlocale(LC_ALL, 0));
705	setlocale(LC_ALL, $cacti_locale);
706	$locale = localeconv();
707
708	if (!isset($locale['decimal_point']) || $locale['decimal_point'] == '') {
709		$locale['decimal_point'] = '.';
710	}
711
712	if (!isset($locale['thousands_sep']) || $locale['thousands_sep'] == '') {
713		$locale['thousands_sep'] = ',';
714	}
715
716	if ($decimals == -1 || $decimals == null) {
717		$number =  number_format($number, null, $locale['decimal_point'], $locale['thousands_sep']);
718	} elseif ($number>=pow($baseu, 4)) {
719		$number =  number_format($number/pow($baseu, 4), $decimals, $locale['decimal_point'], $locale['thousands_sep']) . __(' T');
720	} elseif ($number>=pow($baseu, 3)) {
721		$number = number_format($number/pow($baseu, 3), $decimals, $locale['decimal_point'], $locale['thousands_sep']) . __(' G');
722	} elseif ($number>=pow($baseu, 2)) {
723		$number = number_format($number/pow($baseu, 2), $decimals, $locale['decimal_point'], $locale['thousands_sep']) . __(' M');
724	} elseif ($number>=$baseu) {
725		$number = number_format($number/$baseu, $decimals, $locale['decimal_point'], $locale['thousands_sep']) . __(' K');
726	} else {
727		$number = number_format($number, $decimals, $locale['decimal_point'], $locale['thousands_sep']);
728	}
729
730	foreach ($origlocales as $locale_setting) {
731		if (strpos($locale_setting, '=') !== false) {
732			list($category, $locale) = explode('=', $locale_setting);
733  		} else {
734			$category = LC_ALL;
735			$locale   = $locale_setting;
736		}
737
738		switch($category) {
739		case 'LC_ALL':
740		case 'LC_COLLATE':
741		case 'LC_CTYPE':
742		case 'LC_MONETARY':
743		case 'LC_NUMERIC':
744		case 'LC_TIME':
745			if (defined($category)) {
746				setlocale(constant($category), $locale);
747			}
748		}
749	}
750
751	return $number;
752}
753
754function get_new_user_default_language() {
755	$accepted = repair_locale(read_config_option('i18n_default_language'));
756	if ($accepted == '') {
757		$accepted = repair_locale(read_default_config_option('i18n_default_language'));
758	}
759	return $accepted;
760}
761
762function l10n_debug($text, $mode = FILE_APPEND, $eol = PHP_EOL) {
763	if (is_dir('/share/') && is_writeable('/share/i18n.log')) {
764		file_put_contents('/share/i18n.log', $text . $eol, $mode);
765	} elseif (file_exists('/tmp/i18n.log') && is_writeable('/tmp/i18n.log')) {
766		file_put_contents('/tmp/i18n.log', $text . $eol, $mode);
767	}
768}
769