1<?php 2# MantisBT - A PHP based bugtracking system 3 4# MantisBT is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 2 of the License, or 7# (at your option) any later version. 8# 9# MantisBT is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with MantisBT. If not, see <http://www.gnu.org/licenses/>. 16 17/** 18 * Language (Internationalization) API 19 * 20 * @package CoreAPI 21 * @subpackage LanguageAPI 22 * @copyright Copyright 2000 - 2002 Kenzaburo Ito - kenito@300baud.org 23 * @copyright Copyright 2002 MantisBT Team - mantisbt-dev@lists.sourceforge.net 24 * @link http://www.mantisbt.org 25 * 26 * @uses authentication_api.php 27 * @uses config_api.php 28 * @uses constant_inc.php 29 * @uses error_api.php 30 * @uses plugin_api.php 31 * @uses user_pref_api.php 32 */ 33 34require_api( 'authentication_api.php' ); 35require_api( 'config_api.php' ); 36require_api( 'constant_inc.php' ); 37require_api( 'error_api.php' ); 38require_api( 'plugin_api.php' ); 39require_api( 'user_pref_api.php' ); 40 41# Cache of localization strings in the language specified by the last 42# lang_load call 43$g_lang_strings = array(); 44 45# stack for language overrides 46$g_lang_overrides = array(); 47 48# To be used in custom_strings_inc.php : 49$g_active_language = ''; 50 51/** 52 * Loads the specified language and stores it in $g_lang_strings, to be used by lang_get 53 * @param string $p_lang Name of Language to load. 54 * @param string $p_dir Directory Containing language file. 55 * @return void 56 */ 57function lang_load( $p_lang, $p_dir = null ) { 58 global $g_lang_strings, $g_active_language; 59 60 $g_active_language = $p_lang; 61 if( isset( $g_lang_strings[$p_lang] ) && is_null( $p_dir ) ) { 62 return; 63 } 64 65 if( !lang_language_exists( $p_lang ) ) { 66 return; 67 } 68 69 if( $p_dir === null ) { 70 include_once( config_get_global( 'language_path' ) . 'strings_' . $p_lang . '.txt' ); 71 } else { 72 if( is_file( $p_dir . 'strings_' . $p_lang . '.txt' ) ) { 73 include_once( $p_dir . 'strings_' . $p_lang . '.txt' ); 74 } 75 } 76 77 # Allow overriding strings declared in the language file. 78 # custom_strings_inc.php can use $g_active_language. 79 # Include file multiple times to allow for overrides per language. 80 global $g_config_path; 81 82 if( file_exists( $g_config_path . 'custom_strings_inc.php' ) ) { 83 include( $g_config_path . 'custom_strings_inc.php' ); 84 } 85 86 $t_vars = get_defined_vars(); 87 88 foreach( array_keys( $t_vars ) as $t_var ) { 89 $t_lang_var = preg_replace( '/^s_/', '', $t_var ); 90 if( $t_lang_var != $t_var ) { 91 $g_lang_strings[$p_lang][$t_lang_var] = $$t_var; 92 } else if( 'MANTIS_ERROR' == $t_var ) { 93 if( isset( $g_lang_strings[$p_lang][$t_lang_var] ) ) { 94 foreach( $$t_var as $t_key => $t_val ) { 95 $g_lang_strings[$p_lang][$t_lang_var][$t_key] = $t_val; 96 } 97 } else { 98 $g_lang_strings[$p_lang][$t_lang_var] = $$t_var; 99 } 100 } 101 } 102} 103 104/** 105 * Determine the preferred language 106 * @return string 107 */ 108function lang_get_default() { 109 global $g_active_language; 110 111 $t_lang = false; 112 113 # Confirm that the user's language can be determined 114 if( function_exists( 'auth_is_user_authenticated' ) && auth_is_user_authenticated() ) { 115 $t_lang = user_pref_get_language( auth_get_current_user_id() ); 116 } 117 118 # Otherwise fall back to default 119 if( !$t_lang ) { 120 $t_lang = config_get_global( 'default_language' ); 121 } 122 123 if( $t_lang == 'auto' ) { 124 $t_lang = lang_map_auto(); 125 } 126 127 # Remember the language 128 $g_active_language = $t_lang; 129 130 return $t_lang; 131} 132 133/** 134 * Auto Map Language from HTTP server data 135 * @return string 136 */ 137function lang_map_auto() { 138 $t_lang = config_get_global( 'fallback_language' ); 139 140 if( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { 141 $t_accept_langs = explode( ',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ); 142 $t_auto_map = config_get_global( 'language_auto_map' ); 143 144 # Expand language map 145 $t_auto_map_exp = array(); 146 foreach( $t_auto_map as $t_encs => $t_enc_lang ) { 147 $t_encs_arr = explode( ',', $t_encs ); 148 149 foreach( $t_encs_arr as $t_enc ) { 150 $t_auto_map_exp[trim( $t_enc )] = $t_enc_lang; 151 } 152 } 153 154 # Find encoding 155 foreach( $t_accept_langs as $t_accept_lang ) { 156 $t_tmp = explode( ';', mb_strtolower( $t_accept_lang ) ); 157 158 if( isset( $t_auto_map_exp[trim( $t_tmp[0] )] ) ) { 159 $t_valid_langs = config_get( 'language_choices_arr' ); 160 $t_found_lang = $t_auto_map_exp[trim( $t_tmp[0] )]; 161 162 if( in_array( $t_found_lang, $t_valid_langs, true ) ) { 163 $t_lang = $t_found_lang; 164 break; 165 } 166 } 167 } 168 } 169 170 return $t_lang; 171} 172 173/** 174 * Ensures that a language file has been loaded. 175 * Also load the currently active plugin's strings, if there is one. 176 * @param string $p_lang The language name. 177 * @return void 178 */ 179function lang_ensure_loaded( $p_lang ) { 180 global $g_lang_strings; 181 182 if( !isset( $g_lang_strings[$p_lang] ) ) { 183 lang_load( $p_lang ); 184 } 185 186 # Load current plugin's strings 187 $t_plugin_current = plugin_get_current(); 188 if( $t_plugin_current !== null ) { 189 lang_load( $p_lang, config_get_global( 'plugin_path' ) . $t_plugin_current . '/lang/' ); 190 } 191} 192 193/** 194* Check if the given language exists 195* 196* @param string $p_lang The language name. 197* @return boolean 198*/ 199function lang_language_exists( $p_lang ) { 200 $t_valid_langs = config_get( 'language_choices_arr' ); 201 $t_valid = in_array( $p_lang, $t_valid_langs, true ); 202 return $t_valid; 203} 204 205/** 206 * language stack implementation 207 * push a language onto the stack 208 * @param string $p_lang The language name. 209 * @return void 210 */ 211function lang_push( $p_lang = null ) { 212 global $g_lang_overrides; 213 214 # If no specific language is requested, we'll 215 # try to determine the language from the users 216 # preferences 217 218 $t_lang = $p_lang; 219 220 if( null === $t_lang ) { 221 $t_lang = config_get_global( 'default_language' ); 222 } 223 224 # don't allow 'auto' as a language to be pushed onto the stack 225 # The results from auto are always the local user, not what the 226 # override wants, unless this is the first language setting 227 if( ( 'auto' == $t_lang ) && ( 0 < count( $g_lang_overrides ) ) ) { 228 $t_lang = config_get_global( 'fallback_language' ); 229 } 230 231 $g_lang_overrides[] = $t_lang; 232 233 # Remember the language 234 $g_active_language = $t_lang; 235 236 # make sure it's loaded 237 lang_ensure_loaded( $t_lang ); 238} 239 240/** 241 * pop a language from the stack and return it 242 * @return string 243 */ 244function lang_pop() { 245 global $g_lang_overrides; 246 247 return array_pop( $g_lang_overrides ); 248} 249 250/** 251 * return value on top of the language stack 252 * return default if stack is empty 253 * @return string 254 */ 255function lang_get_current() { 256 global $g_lang_overrides; 257 258 $t_count_overrides = count( $g_lang_overrides ); 259 if( $t_count_overrides > 0 ) { 260 $t_lang = $g_lang_overrides[$t_count_overrides - 1]; 261 } else { 262 $t_lang = lang_get_default(); 263 } 264 265 return $t_lang; 266} 267 268/** 269 * Retrieves an internationalized string 270 * This function will return one of (in order of preference): 271 * 1. The string in the current user's preferred language (if defined) 272 * 2. The string in English 273 * Note: when $p_string is 'MANTIS_ERROR', the return value is an array of error messages. 274 * @param string $p_string The language string to retrieve. 275 * @param string $p_lang The language name. 276 * @return string|array 277 */ 278function lang_get( $p_string, $p_lang = null ) { 279 global $g_lang_strings; 280 281 # If no specific language is requested, we'll try to 282 # determine the language from the users preferences 283 284 $t_lang = $p_lang; 285 286 if( null === $t_lang ) { 287 $t_lang = lang_get_current(); 288 } 289 290 # Now we'll make sure that the requested language is loaded 291 lang_ensure_loaded( $t_lang ); 292 293 # note in the current implementation we always return the same value 294 # because we don't have a concept of falling back on a language. The 295 # language files actually *contain* English strings if none has been 296 # defined in the correct language 297 # @todo thraxisp - not sure if this is still true. Strings from last language loaded 298 # may still be in memory if a new language is loaded. 299 300 if( lang_exists( $p_string, $t_lang ) ) { 301 return $g_lang_strings[$t_lang][$p_string]; 302 } elseif( $t_lang != 'english' ) { 303 # If the string was not found in the foreign language, then retry with English. 304 return lang_get( $p_string, 'english' ); 305 } else { 306 error_parameters( $p_string ); 307 trigger_error( ERROR_LANG_STRING_NOT_FOUND, WARNING ); 308 return $p_string; 309 } 310} 311 312/** 313 * Check the language entry, if found return true, otherwise return false. 314 * @param string $p_string The language string to retrieve. 315 * @param string $p_lang The language name. 316 * @return boolean 317 */ 318function lang_exists( $p_string, $p_lang ) { 319 global $g_lang_strings; 320 321 return( isset( $g_lang_strings[$p_lang] ) && isset( $g_lang_strings[$p_lang][$p_string] ) ); 322} 323 324/** 325 * Get language: 326 * - If found, return the appropriate string (as lang_get()). 327 * - If not found, no default supplied, return the supplied string as is. 328 * - If not found, default supplied, return default. 329 * @param string $p_string The language string to retrieve. 330 * @param string $p_default The default value to return. 331 * @param string $p_lang The language name. 332 * @return string 333 */ 334function lang_get_defaulted( $p_string, $p_default = null, $p_lang = null ) { 335 $t_lang = $p_lang; 336 337 if( null === $t_lang ) { 338 $t_lang = lang_get_current(); 339 } 340 341 # Now we'll make sure that the requested language is loaded 342 lang_ensure_loaded( $t_lang ); 343 344 if( lang_exists( $p_string, $t_lang ) ) { 345 return lang_get( $p_string ); 346 } elseif( $t_lang != 'english' ) { 347 # If the string was not found in the foreign language, then retry with English. 348 return lang_get_defaulted( $p_string, $p_default, 'english' ); 349 } else { 350 # English string was not found either, return the default, 351 # or the original string if no default was provided 352 return $p_default !== null ? $p_default : $p_string; 353 } 354} 355 356/** 357 * Maps current lang string to moment.js locale. 358 * @see https://github.com/moment/moment/tree/develop/locale 359 * @return string Two chars browser language code (e.g. 'de' for German) 360 */ 361function lang_get_current_datetime_locale() { 362 $t_lang = lang_get_current(); 363 364 # Lookup $g_language_auto_map by value and then return the first key 365 $t_auto_map = config_get_global( 'language_auto_map' ); 366 $t_entry = array_search( $t_lang, $t_auto_map ); 367 $t_key_arr = explode( ',', $t_entry ); 368 369 return $t_key_arr[0]; 370} 371