diff --git a/core/includes/language.inc b/core/includes/language.inc index 01d418a..a3932d0 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -2,7 +2,9 @@ /** * @file - * Multiple language handling functionality. + * Language Negotiation API. + * + * @see http://drupal.org/node/1497272 */ /** @@ -11,6 +13,103 @@ const LANGUAGE_NEGOTIATION_DEFAULT = 'language-default'; /** + * @defgroup language_negotiation Language Negotiation API functionality + * @{ + * Functions to customize the language types and the negotiation process. + * + * It is possible to customize the language negotiation process both for how + * Drupal will detect the user's language and also for what type of data they + * are requesting. + * + * The language negotiation API is based on two major concepts: + * - Language types, which describe the types of translatable data. + * - Language methods, which allow Drupal to detect which language it should + * serve to the user. + * + * Drupal defines three built-in language types: + * - Interface language: This is the page's main language. It is used to present + * translated user interface elements such as titles, labels, help text, and + * messages. + * - Content language: This is used to choose in which language to display + * content that is available in more than one language (see the multilingual + * capabilities of the new Field API for details). + * - URL language: This is the language associated with URLs. When generating a + * URL, this value will be used by url() as a default if no explicit + * preference is provided. + * Every language type can have a different set of language methods assigned to + * it. Different language types often share the same methods, but the they can + * have independent methods if needed. + * + * Drupal includes the following language methods: + * - URL: Determine the language from the URL (path prefix or domain). + * - Session: Determine the language from a request/session parameter. + * - User: Follow the user's language preference. + * - Browser: Determine the language from the browser's language settings. + * - Default language: Use the default site language. + * + * The language API allows contributed modules to define additional language + * types through hook_language_types_info() and alter existing language type + * definitions through hook_language_types_info_alter(). + * + * Language types may be configurable or fixed. A configurable language type can + * be adjusted in the user interface where the language methods for that + * language type can be configured. There are also fixed language types that + * have predetermined (module-defined) negotiation settings and, thus, do not + * appear in the configuration page. Here is a code snippet that makes the + * content language (which by default inherits the interface language's + * values) configurable: + * @code + * // Implements hook_language_types_info_alter(). + * function mymodule_language_types_info_alter(&$language_types) { + * unset($language_types[LANGUAGE_TYPE_CONTENT]['fixed']); + * } + * ?> + * @endcode + * + * Every configurable language type will now have its own (independent) language + * configuration. If two language types are configured the same way, their + * language switcher blocks will be functionally identical and will act on + * both language types. + * + * Language methods are simple callback functions that implement a particular + * logic to return a language code. For instance, the URL language method + * searches for a valid path prefix or domain name in the current request URL. + * If a language method does not return a valid language code, the next + * method associated to the language type (based on method weight) is invoked. + * + * Also language methods are module-definable through + * hook_language_negotiation_info() and language method definitions can be + * altered through hook_language_negotiation_info_alter(). Here is an example + * snippet that lets path prefixes be ignored for administrative paths: + * @code + * // Implements hook_language_negotiation_info_alter(). + * function mymodule_language_negotiation_info_alter(&$negotiation_info) { + *   $negotiation_info[LOCALE_LANGUAGE_NEGOTIATION_URL]['callbacks'] + * ['negotiation'] = 'mymodule_from_url'; + *   $negotiation_info[LOCALE_LANGUAGE_NEGOTIATION_URL]['file'] = + * drupal_get_path('module', 'mymodule') . '/mymodule.module'; + * } + * + * // Identify language via URL prefix or domain excluding administrative paths. + * function mymodule_from_url($languages) { + *   // Use the core URL language method to get a valid language code. + *   require DRUPAL_ROOT . '/includes/locale.inc'; + *   $langcode = locale_language_from_url($languages); + * + *   // If we have an administrative path just return the default language. + *   if (isset($_GET['q']) && strtok($_GET['q'], '/') == 'admin') { + *     return language_default()->language; + *   } + * + *   return $langcode; + * } + * ?> + * @endcode + * + * @link http://drupal.org/node/1497272 Language Negotiation API @endlink + */ + +/** * Chooses a language for the given type based on language negotiation settings. * * @param $type @@ -20,8 +119,8 @@ const LANGUAGE_NEGOTIATION_DEFAULT = 'language-default'; * The negotiated language object. */ function language_types_initialize($type) { - // Execute the language negotiation methods in the order they were set up and - // return the first valid language found. + // Execute the language methods in the order they were set up and return the + // first valid language found. $negotiation = variable_get("language_negotiation_$type", array()); foreach ($negotiation as $method_id => $method) { @@ -158,7 +257,7 @@ function language_types_set() { } /** - * Returns the ID of the language type's first language negotiation method. + * Returns the ID of the language type's first language method. * * @param $type * The language type. @@ -169,10 +268,10 @@ function language_negotiation_method_get_first($type) { } /** - * Checks if a language negotiation method is enabled for a language type. + * Checks if a language method is enabled for a language type. * * @param $method_id - * The language negotiation method ID. + * The language method ID. * @param $type * (optional) The language type. If none is passed, all the configurable * language types will be inspected. @@ -231,7 +330,7 @@ function language_negotiation_get_switch_links($type, $path) { } /** - * Removes any language negotiation methods that are no longer defined. + * Removes any language methods that are no longer defined. */ function language_negotiation_purge() { // Ensure that we are getting the defined language negotiation information. An @@ -254,12 +353,12 @@ function language_negotiation_purge() { } /** - * Saves a list of language negotiation methods for a language type. + * Saves a list of language methods for a language type. * * @param $type * The language type. * @param $method_weights - * An array of language negotiation method weights keyed by method id. + * An array of language method weights keyed by method id. */ function language_negotiation_set($type, $method_weights) { // Save only the necessary fields. @@ -269,17 +368,16 @@ function language_negotiation_set($type, $method_weights) { $negotiation_info = language_negotiation_info(); $default_types = language_types_get_configurable(FALSE); - // Order the language negotiation method list by weight. + // Order the language method list by weight. asort($method_weights); foreach ($method_weights as $method_id => $weight) { if (isset($negotiation_info[$method_id])) { $method = $negotiation_info[$method_id]; - // If the language negotiation method does not express any preference - // about types, make it available for any configurable type. + // If the language method does not express any preference about types, + // make it available for any configurable type. $types = array_flip(isset($method['types']) ? $method['types'] : $default_types); - // Check if the language negotiation method is defined and has the right - // type. + // Check if the language method is defined and has the right type. if (isset($types[$type])) { $method_data = array(); foreach ($method_fields as $field) { @@ -296,19 +394,19 @@ function language_negotiation_set($type, $method_weights) { } /** - * Returns all defined language negotiation methods. + * Returns all defined language methods. * * @return - * An array of language negotiation methods. + * An array of language methods. */ function language_negotiation_info() { $negotiation_info = &drupal_static(__FUNCTION__); if (!isset($negotiation_info)) { - // Collect all the module-defined language negotiation methods. + // Collect all the module-defined language methods. $negotiation_info = module_invoke_all('language_negotiation_info'); - // Add the default language negotiation method. + // Add the default language method. $negotiation_info[LANGUAGE_NEGOTIATION_DEFAULT] = array( 'callbacks' => array('language' => 'language_from_default'), 'weight' => 10, @@ -317,7 +415,7 @@ function language_negotiation_info() { 'config' => 'admin/config/regional/language', ); - // Let other modules alter the list of language negotiation methods. + // Let other modules alter the list of language methods. drupal_alter('language_negotiation_info', $negotiation_info); } @@ -325,16 +423,16 @@ function language_negotiation_info() { } /** - * Invokes a language negotiation method and caches the results. + * Invokes a language method and caches the results. * * @param $method_id - * The language negotiation method ID. + * The language method ID. * @param $method - * (optional) The language negotiation method to be invoked. If not passed it - * will be explicitly loaded through language_negotiation_info(). + * (optional) The language method to be invoked. If not passed it will be + * explicitly loaded through language_negotiation_info(). * * @return - * The language negotiation method's return value. + * The language method's return value. */ function language_negotiation_method_invoke($method_id, $method = NULL) { $results = &drupal_static(__FUNCTION__); @@ -354,8 +452,8 @@ function language_negotiation_method_invoke($method_id, $method = NULL) { require_once DRUPAL_ROOT . '/' . $method['file']; } - // If the language negotiation method has no cache preference or this is - // satisfied we can execute the callback. + // If the language method has no cache preference or this is satisfied we + // can execute the callback. $cache = !isset($method['cache']) || $user->uid || $method['cache'] == variable_get('cache', 0); $callback = isset($method['callbacks']['negotiation']) ? $method['callbacks']['negotiation'] : FALSE; $langcode = $cache && function_exists($callback) ? $callback($languages) : FALSE; @@ -363,9 +461,9 @@ function language_negotiation_method_invoke($method_id, $method = NULL) { } // Since objects are resources we need to return a clone to prevent the - // language negotiation method cache to be unintentionally altered. The same - // language negotiation methods might be used with different language types - // based on configuration. + // language method cache to be unintentionally altered. The same language + // negotiation methods might be used with different language types based + // on configuration. return !empty($results[$method_id]) ? clone($results[$method_id]) : $results[$method_id]; } @@ -382,8 +480,8 @@ function language_from_default() { /** * Split the given path into prefix and actual path. * - * Parse the given path and return the language object identified by the - * prefix and the actual path. + * Parse the given path and return the language object identified by the prefix + * and the actual path. * * @param $path * The path to split. @@ -435,3 +533,7 @@ function language_fallback_get_candidates($type = LANGUAGE_TYPE_CONTENT) { return $fallback_candidates; } + +/** + * @} End of "language_negotiation" + */ diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php index 9f28619..299422e 100644 --- a/core/modules/system/language.api.php +++ b/core/modules/system/language.api.php @@ -74,10 +74,12 @@ function hook_language_switch_links_alter(array &$links, $type, $path) { * following key-value pairs: * - "name": The human-readable language type identifier. * - "description": A description of the language type. - * - "fixed": A fixed array of language negotiation method identifiers to use - * to initialize this language. Defining this key makes the language type + * - "fixed": A fixed array of language method identifiers to use to + * initialize this language. Defining this key makes the language type * non-configurable and will always use the specified methods in the given * priority order. + * + * @ingroup language_negotiation */ function hook_language_types_info() { return array( @@ -98,6 +100,8 @@ function hook_language_types_info() { * * @param $language_types * Array of language type definitions. + * + * @ingroup language_negotiation */ function hook_language_types_info_alter(array &$language_types) { if (isset($language_types['custom_language_type'])) { @@ -106,12 +110,20 @@ function hook_language_types_info_alter(array &$language_types) { } /** - * Allow modules to define their own language negotiation methods. + * Allow modules to define their own language methods. + * + * Language method definitions may include two more callbacks besides the + * language method itself: + * - If the language method can take advantage of a language switcher block, + * the switcher callback will allow it to return the language switch links + * that suit its logic, see locale_language_switcher_url() for an example. + * - If the language method needs to rewrite URLs, it can specify an + * url_rewrite callback which will provide the rewriting logic. * * @return - * An array of language negotiation method definitions. Each method has an - * identifier key. The language negotiation method definition is an indexed - * array that may contain the following key-value pairs: + * An array of language method definitions. Each method has an identifier + * key. The language method definition is an indexed array that may contain + * the following key-value pairs: * - "types": An array of allowed language types. If a language negotiation * method does not specify which language types it should be used with, it * will be available for all the configurable language types. @@ -124,13 +136,15 @@ function hook_language_types_info_alter(array &$language_types) { * - "url_rewrite": The callback that will provide URL rewriting. * - "file": A file that will be included before the callback is invoked; this * allows callback functions to be in separate files. - * - "weight": The default weight the language negotiation method has. + * - "weight": The default weight the language method has. * - "name": A human-readable identifier. - * - "description": A description of the language negotiation method. - * - "config": An internal path pointing to the language negotiation method - * configuration page. + * - "description": A description of the language method. + * - "config": An internal path pointing to the language method configuration + * page. * - "cache": The value Drupal's page cache should be set to for the current - * language negotiation method to be invoked. + * language method to be invoked. + * + * @ingroup language_negotiation */ function hook_language_negotiation_info() { return array( @@ -143,18 +157,20 @@ function hook_language_negotiation_info() { 'file' => drupal_get_path('module', 'custom') . '/custom.module', 'weight' => -4, 'types' => array('custom_language_type'), - 'name' => t('Custom language negotiation method'), - 'description' => t('This is a custom language negotiation method.'), + 'name' => t('Custom language method'), + 'description' => t('This is a custom language method.'), 'cache' => 0, ), ); } /** - * Perform alterations on language negotiation methods. + * Perform alterations on language methods. * * @param $negotiation_info - * Array of language negotiation method definitions. + * Array of language method definitions. + * + * @ingroup language_negotiation */ function hook_language_negotiation_info_alter(array &$negotiation_info) { if (isset($negotiation_info['custom_language_method'])) {