| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Multiple language handling functionality. 6 */ 7 8 /** 9 * No language negotiation. The default language is used. 10 */ 11 define('LANGUAGE_NEGOTIATION_DEFAULT', 'language-default'); 12 13 /** 14 * Return all the defined language types. 15 * 16 * @return 17 * An array of language type names. The name will be used as the global 18 * variable name the language value will be stored in. 19 */ 20 function language_types_info() { 21 $language_types = &drupal_static(__FUNCTION__); 22 23 if (!isset($language_types)) { 24 $language_types = module_invoke_all('language_types_info'); 25 // Let other modules alter the list of language types. 26 drupal_alter('language_types_info', $language_types); 27 } 28 29 return $language_types; 30 } 31 32 /** 33 * Return only the configurable language types. 34 * 35 * A language type maybe configurable or fixed. A fixed language type is a type 36 * whose negotiation values are unchangeable and defined while defining the 37 * language type itself. 38 * 39 * @param $stored 40 * Optional. By default retrieves values from the 'language_types' variable to 41 * avoid unnecessary hook invocations. 42 * If set to FALSE retrieves values from the actual language type definitions. 43 * This allows to react to alterations performed on the definitions by modules 44 * installed after the 'language_types' variable is set. 45 * 46 * @return 47 * An array of language type names. 48 */ 49 function language_types_configurable($stored = TRUE) { 50 $configurable = &drupal_static(__FUNCTION__); 51 52 if ($stored && !isset($configurable)) { 53 $types = variable_get('language_types', drupal_language_types()); 54 $configurable = array_keys(array_filter($types)); 55 } 56 57 if (!$stored) { 58 $result = array(); 59 foreach (language_types_info() as $type => $info) { 60 if (!isset($info['fixed'])) { 61 $result[] = $type; 62 } 63 } 64 return $result; 65 } 66 67 return $configurable; 68 } 69 70 /** 71 * Disable the given language types. 72 * 73 * @param $types 74 * An array of language types. 75 */ 76 function language_types_disable($types) { 77 $enabled_types = variable_get('language_types', drupal_language_types()); 78 79 foreach ($types as $type) { 80 unset($enabled_types[$type]); 81 } 82 83 variable_set('language_types', $enabled_types); 84 } 85 86 /** 87 * Updates the language type configuration. 88 */ 89 function language_types_set() { 90 // Ensure that we are getting the defined language negotiation information. An 91 // invocation of module_enable() or module_disable() could outdate the cached 92 // information. 93 drupal_static_reset('language_types_info'); 94 drupal_static_reset('language_negotiation_info'); 95 96 // Determine which language types are configurable and which not by checking 97 // whether the 'fixed' key is defined. Non-configurable (fixed) language types 98 // have their language negotiation settings stored there. 99 $defined_providers = language_negotiation_info(); 100 foreach (language_types_info() as $type => $info) { 101 if (isset($info['fixed'])) { 102 $language_types[$type] = FALSE; 103 $negotiation = array(); 104 foreach ($info['fixed'] as $weight => $id) { 105 if (isset($defined_providers[$id])) { 106 $negotiation[$id] = $weight; 107 } 108 } 109 language_negotiation_set($type, $negotiation); 110 } 111 else { 112 $language_types[$type] = TRUE; 113 } 114 } 115 116 // Save language types. 117 variable_set('language_types', $language_types); 118 119 // Ensure that subsequent calls of language_types_configurable() return the 120 // updated language type information. 121 drupal_static_reset('language_types_configurable'); 122 } 123 124 /** 125 * Check if a language provider is enabled. 126 * 127 * This has two possible behaviors: 128 * - If $provider_id is given return its ID if enabled, FALSE otherwise. 129 * - If no ID is passed the first enabled language provider is returned. 130 * 131 * @param $type 132 * The language negotiation type. 133 * @param $provider_id 134 * The language provider ID. 135 * 136 * @return 137 * The provider ID if it is enabled, FALSE otherwise. 138 */ 139 function language_negotiation_get($type, $provider_id = NULL) { 140 $negotiation = variable_get("language_negotiation_$type", array()); 141 142 if (empty($negotiation)) { 143 return empty($provider_id) ? LANGUAGE_NEGOTIATION_DEFAULT : FALSE; 144 } 145 146 if (empty($provider_id)) { 147 return key($negotiation); 148 } 149 150 if (isset($negotiation[$provider_id])) { 151 return $provider_id; 152 } 153 154 return FALSE; 155 } 156 157 /** 158 * Check if the given language provider is enabled for any configurable language 159 * type. 160 * 161 * @param $provider_id 162 * The language provider ID. 163 * 164 * @return 165 * TRUE if there is at least one language type for which the give language 166 * provider is enabled, FALSE otherwise. 167 */ 168 function language_negotiation_get_any($provider_id) { 169 foreach (language_types_configurable() as $type) { 170 if (language_negotiation_get($type, $provider_id)) { 171 return TRUE; 172 } 173 } 174 175 return FALSE; 176 } 177 178 /** 179 * Return the language switch links for the given language. 180 * 181 * @param $type 182 * The language negotiation type. 183 * @param $path 184 * The internal path the switch links will be relative to. 185 * 186 * @return 187 * A keyed array of links ready to be themed. 188 */ 189 function language_negotiation_get_switch_links($type, $path) { 190 $links = FALSE; 191 $negotiation = variable_get("language_negotiation_$type", array()); 192 193 // Only get the languages if we have more than one. 194 if (count(language_list()) >= 2) { 195 $language = language_initialize($type); 196 } 197 198 foreach ($negotiation as $id => $provider) { 199 if (isset($provider['callbacks']['switcher'])) { 200 if (isset($provider['file'])) { 201 require_once DRUPAL_ROOT . '/' . $provider['file']; 202 } 203 204 $callback = $provider['callbacks']['switcher']; 205 $result = $callback($type, $path); 206 207 // Add support for WCAG 2.0's Language of Parts to add language identifiers. 208 // http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html 209 foreach ($result as $langcode => $link) { 210 $result[$langcode]['attributes']['lang'] = $langcode; 211 } 212 213 if (!empty($result)) { 214 // Allow modules to provide translations for specific links. 215 drupal_alter('language_switch_links', $result, $type, $path); 216 $links = (object) array('links' => $result, 'provider' => $id); 217 break; 218 } 219 } 220 } 221 222 return $links; 223 } 224 225 /** 226 * Updates language configuration to remove any language provider that is no longer defined. 227 */ 228 function language_negotiation_purge() { 229 // Ensure that we are getting the defined language negotiation information. An 230 // invocation of module_enable() or module_disable() could outdate the cached 231 // information. 232 drupal_static_reset('language_negotiation_info'); 233 drupal_static_reset('language_types_info'); 234 235 $defined_providers = language_negotiation_info(); 236 foreach (language_types_info() as $type => $type_info) { 237 $weight = 0; 238 $negotiation = array(); 239 foreach (variable_get("language_negotiation_$type", array()) as $id => $provider) { 240 if (isset($defined_providers[$id])) { 241 $negotiation[$id] = $weight++; 242 } 243 } 244 language_negotiation_set($type, $negotiation); 245 } 246 } 247 248 /** 249 * Save a list of language providers. 250 * 251 * @param $type 252 * The language negotiation type. 253 * @param $language_providers 254 * An array of language provider weights keyed by id. 255 * @see language_provider_weight() 256 */ 257 function language_negotiation_set($type, $language_providers) { 258 // Save only the necessary fields. 259 $provider_fields = array('callbacks', 'file', 'cache'); 260 261 $negotiation = array(); 262 $providers_weight = array(); 263 $defined_providers = language_negotiation_info(); 264 $default_types = language_types_configurable(FALSE); 265 266 // Initialize the providers weight list. 267 foreach ($language_providers as $id => $provider) { 268 $providers_weight[$id] = language_provider_weight($provider); 269 } 270 271 // Order providers list by weight. 272 asort($providers_weight); 273 274 foreach ($providers_weight as $id => $weight) { 275 if (isset($defined_providers[$id])) { 276 $provider = $defined_providers[$id]; 277 // If the provider does not express any preference about types, make it 278 // available for any configurable type. 279 $types = array_flip(isset($provider['types']) ? $provider['types'] : $default_types); 280 // Check if the provider is defined and has the right type. 281 if (isset($types[$type])) { 282 $provider_data = array(); 283 foreach ($provider_fields as $field) { 284 if (isset($provider[$field])) { 285 $provider_data[$field] = $provider[$field]; 286 } 287 } 288 $negotiation[$id] = $provider_data; 289 } 290 } 291 } 292 293 variable_set("language_negotiation_$type", $negotiation); 294 } 295 296 /** 297 * Return all the defined language providers. 298 * 299 * @return 300 * An array of language providers. 301 */ 302 function language_negotiation_info() { 303 $language_providers = &drupal_static(__FUNCTION__); 304 305 if (!isset($language_providers)) { 306 // Collect all the module-defined language negotiation providers. 307 $language_providers = module_invoke_all('language_negotiation_info'); 308 309 // Add the default language provider. 310 $language_providers[LANGUAGE_NEGOTIATION_DEFAULT] = array( 311 'callbacks' => array('language' => 'language_from_default'), 312 'weight' => 10, 313 'name' => t('Default'), 314 'description' => t('Use the default site language (@language_name).', array('@language_name' => language_default()->native)), 315 ); 316 317 // Let other modules alter the list of language providers. 318 drupal_alter('language_negotiation_info', $language_providers); 319 } 320 321 return $language_providers; 322 } 323 324 /** 325 * Helper function used to cache the language providers results. 326 * 327 * @param $provider_id 328 * The language provider ID. 329 * @param $provider 330 * The language provider to be invoked. If not passed it will be explicitly 331 * loaded through language_negotiation_info(). 332 * 333 * @return 334 * The language provider's return value. 335 */ 336 function language_provider_invoke($provider_id, $provider = NULL) { 337 $results = &drupal_static(__FUNCTION__); 338 339 if (!isset($results[$provider_id])) { 340 global $user; 341 342 // Get languages grouped by status and select only the enabled ones. 343 $languages = language_list('enabled'); 344 $languages = $languages[1]; 345 346 if (!isset($provider)) { 347 $providers = language_negotiation_info(); 348 $provider = $providers[$provider_id]; 349 } 350 351 if (isset($provider['file'])) { 352 require_once DRUPAL_ROOT . '/' . $provider['file']; 353 } 354 355 // If the language provider has no cache preference or this is satisfied 356 // we can execute the callback. 357 $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == variable_get('cache', 0); 358 $callback = isset($provider['callbacks']['language']) ? $provider['callbacks']['language'] : FALSE; 359 $langcode = $cache && function_exists($callback) ? $callback($languages) : FALSE; 360 $results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE; 361 } 362 363 // Since objects are resources we need to return a clone to prevent the 364 // provider cache to be unintentionally altered. The same providers might be 365 // used with different language types based on configuration. 366 return !empty($results[$provider_id]) ? clone($results[$provider_id]) : $results[$provider_id]; 367 } 368 369 /** 370 * Return the passed language provider weight or a default value. 371 * 372 * @param $provider 373 * A language provider data structure. 374 * 375 * @return 376 * A numeric weight. 377 */ 378 function language_provider_weight($provider) { 379 $default = is_numeric($provider) ? $provider : 0; 380 return isset($provider['weight']) && is_numeric($provider['weight']) ? $provider['weight'] : $default; 381 } 382 383 /** 384 * Choose a language for the given type based on language negotiation settings. 385 * 386 * @param $type 387 * The language type. 388 * 389 * @return 390 * The negotiated language object. 391 */ 392 function language_initialize($type) { 393 // Execute the language providers in the order they were set up and return the 394 // first valid language found. 395 $negotiation = variable_get("language_negotiation_$type", array()); 396 397 foreach ($negotiation as $provider_id => $provider) { 398 $language = language_provider_invoke($provider_id, $provider); 399 if ($language) { 400 $language->provider = $provider_id; 401 return $language; 402 } 403 } 404 405 // If no other language was found use the default one. 406 $language = language_default(); 407 $language->provider = LANGUAGE_NEGOTIATION_DEFAULT; 408 return $language; 409 } 410 411 /** 412 * Default language provider. 413 * 414 * @return 415 * The default language code. 416 */ 417 function language_from_default() { 418 return language_default()->language; 419 } 420 421 /** 422 * Splits the given path into prefix and actual path. 423 * 424 * Parse the given path and return the language object identified by the 425 * prefix and the actual path. 426 * 427 * @param $path 428 * The path to split. 429 * @param $languages 430 * An array of valid languages. 431 * 432 * @return 433 * An array composed of: 434 * - A language object corresponding to the identified prefix on success, 435 * FALSE otherwise. 436 * - The path without the prefix on success, the given path otherwise. 437 */ 438 function language_url_split_prefix($path, $languages) { 439 $args = empty($path) ? array() : explode('/', $path); 440 $prefix = array_shift($args); 441 442 // Search prefix within enabled languages. 443 foreach ($languages as $language) { 444 if (!empty($language->prefix) && $language->prefix == $prefix) { 445 // Rebuild $path with the language removed. 446 return array($language, implode('/', $args)); 447 } 448 } 449 450 return array(FALSE, $path); 451 } 452 453 /** 454 * Returns the possible fallback languages ordered by language weight. 455 * 456 * @param 457 * (optional) The language type. Defaults to LANGUAGE_TYPE_CONTENT. 458 * 459 * @return 460 * An array of language codes. 461 */ 462 function language_fallback_get_candidates($type = LANGUAGE_TYPE_CONTENT) { 463 $fallback_candidates = &drupal_static(__FUNCTION__); 464 465 if (!isset($fallback_candidates)) { 466 $fallback_candidates = array(); 467 468 // Get languages ordered by weight. 469 // Use array keys to avoid duplicated entries. 470 foreach (language_list('weight') as $languages) { 471 foreach ($languages as $language) { 472 $fallback_candidates[$language->language] = NULL; 473 } 474 } 475 476 $fallback_candidates = array_keys($fallback_candidates); 477 $fallback_candidates[] = LANGUAGE_NONE; 478 479 // Let other modules hook in and add/change candidates. 480 drupal_alter('language_fallback_candidates', $fallback_candidates); 481 } 482 483 return $fallback_candidates; 484 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title