Commit 661488af authored by Dries Buytaert's avatar Dries Buytaert
Browse files

- Patch #1222106 by Gábor Hojtsy, plach, xjm: Unify language negotiation APIs,...

- Patch #1222106 by Gábor Hojtsy, plach, xjm: Unify language negotiation APIs, declutter terminology.
parent cf77b6e8
Loading
Loading
Loading
Loading
+110 −147
Original line number Diff line number Diff line
@@ -20,22 +20,22 @@
 *   The negotiated language object.
 */
function language_types_initialize($type) {
  // Execute the language providers in the order they were set up and return the
  // first valid language found.
  // Execute the language negotiation 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 $provider_id => $provider) {
    $language = language_provider_invoke($provider_id, $provider);
  foreach ($negotiation as $method_id => $method) {
    $language = language_negotiation_method_invoke($method_id, $method);
    if ($language) {
      // Remember the provider key used to detect the language.
      $language->provider = $provider_id;
      // Remember the method ID used to detect the language.
      $language->method_id = $method_id;
      return $language;
    }
  }

  // If no other language was found use the default one.
  $language = language_default();
  $language->provider = LANGUAGE_NEGOTIATION_DEFAULT;
  $language->method_id = LANGUAGE_NEGOTIATION_DEFAULT;
  return $language;
}

@@ -68,11 +68,11 @@ function language_types_info() {
 * language type itself.
 *
 * @param $stored
 *   Optional. By default retrieves values from the 'language_types' variable to
 *   avoid unnecessary hook invocations.
 *   If set to FALSE retrieves values from the actual language type definitions.
 *   This allows to react to alterations performed on the definitions by modules
 *   installed after the 'language_types' variable is set.
 *   (optional) By default, retrieves values from the 'language_types' variable
 *   to avoid unnecessary hook invocations. If set to FALSE, retrieves values
 *   from the actual language type definitions. This allows reaction to
 *   alterations performed on the definitions by modules installed after the
 *   'language_types' variable is set.
 *
 * @return
 *   An array of language type names.
@@ -99,7 +99,7 @@ function language_types_get_configurable($stored = TRUE) {
}

/**
 * Disable the given language types.
 * Disables the given language types.
 *
 * @param $types
 *   An array of language types.
@@ -128,17 +128,17 @@ function language_types_set() {
  // whether the 'fixed' key is defined. Non-configurable (fixed) language types
  // have their language negotiation settings stored there.
  $language_types = array();
  $defined_providers = language_negotiation_info();
  $negotiation_info = language_negotiation_info();
  foreach (language_types_info() as $type => $info) {
    if (isset($info['fixed'])) {
      $language_types[$type] = FALSE;
      $negotiation = array();
      foreach ($info['fixed'] as $weight => $id) {
        if (isset($defined_providers[$id])) {
          $negotiation[$id] = $weight;
      $method_weights = array();
      foreach ($info['fixed'] as $weight => $method_id) {
        if (isset($negotiation_info[$method_id])) {
          $method_weights[$method_id] = $weight;
        }
      }
      language_negotiation_set($type, $negotiation);
      language_negotiation_set($type, $method_weights);
    }
    else {
      $language_types[$type] = TRUE;
@@ -154,52 +154,35 @@ function language_types_set() {
}

/**
 * Check if a language provider is enabled.
 *
 * This has two possible behaviors:
 *  - If $provider_id is given return its ID if enabled, FALSE otherwise.
 *  - If no ID is passed the first enabled language provider is returned.
 * Returns the ID of the language type's first language negotiation method.
 *
 * @param $type
 *   The language negotiation type.
 * @param $provider_id
 *   The language provider ID.
 *
 * @return
 *   The provider ID if it is enabled, FALSE otherwise.
 *   The language type.
 */
function language_negotiation_get($type, $provider_id = NULL) {
function language_negotiation_method_get_first($type) {
  $negotiation = variable_get("language_negotiation_$type", array());

  if (empty($negotiation)) {
    return empty($provider_id) ? LANGUAGE_NEGOTIATION_DEFAULT : FALSE;
  }

  if (empty($provider_id)) {
    return key($negotiation);
  }

  if (isset($negotiation[$provider_id])) {
    return $provider_id;
  }

  return FALSE;
  return empty($negotiation) ? LANGUAGE_NEGOTIATION_DEFAULT : key($negotiation);
}

/**
 * Check if the given language provider is enabled for any configurable language
 * type.
 * Checks if a language negotiation method is enabled for a language type.
 *
 * @param $provider_id
 *   The language provider ID.
 * @param $method_id
 *   The language negotiation method ID.
 * @param $type
 *   (optional) The language type. If none is passed, all the configurable
 *   language types will be inspected.
 *
 * @return
 *   TRUE if there is at least one language type for which the give language
 *   provider is enabled, FALSE otherwise.
 *   TRUE if the method is enabled for at least one of the given language
 *   types, or FALSE otherwise.
 */
function language_negotiation_get_any($provider_id) {
  foreach (language_types_get_configurable() as $type) {
    if (language_negotiation_get($type, $provider_id)) {
function language_negotiation_method_enabled($method_id, $type = NULL) {
  $language_types = !empty($type) ? array($type) : language_types_get_configurable();

  foreach ($language_types as $type) {
    $negotiation = variable_get("language_negotiation_$type", array());
    if (isset($negotiation[$method_id])) {
      return TRUE;
    }
  }
@@ -208,10 +191,10 @@ function language_negotiation_get_any($provider_id) {
}

/**
 * Return the language switch links for the given language.
 * Returns the language switch links for the given language type.
 *
 * @param $type
 *   The language negotiation type.
 *   The language type.
 * @param $path
 *   The internal path the switch links will be relative to.
 *
@@ -222,19 +205,19 @@ function language_negotiation_get_switch_links($type, $path) {
  $links = FALSE;
  $negotiation = variable_get("language_negotiation_$type", array());

  foreach ($negotiation as $id => $provider) {
    if (isset($provider['callbacks']['switcher'])) {
      if (isset($provider['file'])) {
        require_once DRUPAL_ROOT . '/' . $provider['file'];
  foreach ($negotiation as $method_id => $method) {
    if (isset($method['callbacks']['language_switch'])) {
      if (isset($method['file'])) {
        require_once DRUPAL_ROOT . '/' . $method['file'];
      }

      $callback = $provider['callbacks']['switcher'];
      $callback = $method['callbacks']['language_switch'];
      $result = $callback($type, $path);

      if (!empty($result)) {
        // Allow modules to provide translations for specific links.
        drupal_alter('language_switch_links', $result, $type, $path);
        $links = (object) array('links' => $result, 'provider' => $id);
        $links = (object) array('links' => $result, 'method_id' => $method_id);
        break;
      }
    }
@@ -244,7 +227,7 @@ function language_negotiation_get_switch_links($type, $path) {
}

/**
 * Updates language configuration to remove any language provider that is no longer defined.
 * Removes any language negotiation methods that are no longer defined.
 */
function language_negotiation_purge() {
  // Ensure that we are getting the defined language negotiation information. An
@@ -253,60 +236,54 @@ function language_negotiation_purge() {
  drupal_static_reset('language_negotiation_info');
  drupal_static_reset('language_types_info');

  $defined_providers = language_negotiation_info();
  $negotiation_info = language_negotiation_info();
  foreach (language_types_info() as $type => $type_info) {
    $weight = 0;
    $negotiation = array();
    foreach (variable_get("language_negotiation_$type", array()) as $id => $provider) {
      if (isset($defined_providers[$id])) {
        $negotiation[$id] = $weight++;
    $method_weights = array();
    foreach (variable_get("language_negotiation_$type", array()) as $method_id => $method) {
      if (isset($negotiation_info[$method_id])) {
        $method_weights[$method_id] = $weight++;
      }
    }
    language_negotiation_set($type, $negotiation);
    language_negotiation_set($type, $method_weights);
  }
}

/**
 * Save a list of language providers.
 * Saves a list of language negotiation methods for a language type.
 *
 * @param $type
 *   The language negotiation type.
 * @param $language_providers
 *   An array of language provider weights keyed by id.
 *   @see language_provider_weight()
 *   The language type.
 * @param $method_weights
 *   An array of language negotiation method weights keyed by method id.
 */
function language_negotiation_set($type, $language_providers) {
function language_negotiation_set($type, $method_weights) {
  // Save only the necessary fields.
  $provider_fields = array('callbacks', 'file', 'cache');
  $method_fields = array('callbacks', 'file', 'cache');

  $negotiation = array();
  $providers_weight = array();
  $defined_providers = language_negotiation_info();
  $negotiation_info = language_negotiation_info();
  $default_types = language_types_get_configurable(FALSE);

  // Initialize the providers weight list.
  foreach ($language_providers as $id => $provider) {
    $providers_weight[$id] = language_provider_weight($provider);
  }

  // Order providers list by weight.
  asort($providers_weight);

  foreach ($providers_weight as $id => $weight) {
    if (isset($defined_providers[$id])) {
      $provider = $defined_providers[$id];
      // If the provider does not express any preference about types, make it
      // available for any configurable type.
      $types = array_flip(isset($provider['types']) ? $provider['types'] : $default_types);
      // Check if the provider is defined and has the right type.
  // Order the language negotiation 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.
      $types = array_flip(isset($method['types']) ? $method['types'] : $default_types);
      // Check if the language negotiation method is defined and has the right
      // type.
      if (isset($types[$type])) {
        $provider_data = array();
        foreach ($provider_fields as $field) {
          if (isset($provider[$field])) {
            $provider_data[$field] = $provider[$field];
        $method_data = array();
        foreach ($method_fields as $field) {
          if (isset($method[$field])) {
            $method_data[$field] = $method[$field];
          }
        }
        $negotiation[$id] = $provider_data;
        $negotiation[$method_id] = $method_data;
      }
    }
  }
@@ -315,20 +292,20 @@ function language_negotiation_set($type, $language_providers) {
}

/**
 * Return all the defined language providers.
 * Returns all defined language negotiation methods.
 *
 * @return
 *   An array of language providers.
 *   An array of language negotiation methods.
 */
function language_negotiation_info() {
  $language_providers = &drupal_static(__FUNCTION__);
  $negotiation_info = &drupal_static(__FUNCTION__);

  if (!isset($language_providers)) {
    // Collect all the module-defined language negotiation providers.
    $language_providers = module_invoke_all('language_negotiation_info');
  if (!isset($negotiation_info)) {
    // Collect all the module-defined language negotiation methods.
    $negotiation_info = module_invoke_all('language_negotiation_info');

    // Add the default language provider.
    $language_providers[LANGUAGE_NEGOTIATION_DEFAULT] = array(
    // Add the default language negotiation method.
    $negotiation_info[LANGUAGE_NEGOTIATION_DEFAULT] = array(
      'callbacks' => array('language' => 'language_from_default'),
      'weight' => 10,
      'name' => t('Default language'),
@@ -336,74 +313,60 @@ function language_negotiation_info() {
      'config' => 'admin/config/regional/language',
    );

    // Let other modules alter the list of language providers.
    drupal_alter('language_negotiation_info', $language_providers);
     // Let other modules alter the list of language negotiation methods.
     drupal_alter('language_negotiation_info', $negotiation_info);
  }

  return $language_providers;
  return $negotiation_info;
}

/**
 * Helper function used to cache the language providers results.
 * Invokes a language negotiation method and caches the results.
 *
 * @param $provider_id
 *   The language provider ID.
 * @param $provider
 *   The language provider to be invoked. If not passed it will be explicitly
 *   loaded through language_negotiation_info().
 * @param $method_id
 *   The language negotiation method ID.
 * @param $method
 *   (optional) The language negotiation method to be invoked. If not passed it
 *   will be explicitly loaded through language_negotiation_info().
 *
 * @return
 *   The language provider's return value.
 *   The language negotiation method's return value.
 */
function language_provider_invoke($provider_id, $provider = NULL) {
function language_negotiation_method_invoke($method_id, $method = NULL) {
  $results = &drupal_static(__FUNCTION__);

  if (!isset($results[$provider_id])) {
  if (!isset($results[$method_id])) {
    global $user;

    // Get the enabled languages only.
    $languages = language_list(TRUE);

    if (!isset($provider)) {
      $providers = language_negotiation_info();
      $provider = $providers[$provider_id];
    if (!isset($method)) {
      $negotiation_info = language_negotiation_info();
      $method = $negotiation_info[$method_id];
    }

    if (isset($provider['file'])) {
      require_once DRUPAL_ROOT . '/' . $provider['file'];
    if (isset($method['file'])) {
      require_once DRUPAL_ROOT . '/' . $method['file'];
    }

    // If the language provider has no cache preference or this is satisfied
    // we can execute the callback.
    $config = config('system.performance');
    $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == $config->get('cache');
    $callback = isset($provider['callbacks']['language']) ? $provider['callbacks']['language'] : FALSE;
    // If the language negotiation 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;
    $results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
    $results[$method_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
  }

  // Since objects are resources we need to return a clone to prevent the
  // provider cache to be unintentionally altered. The same providers might be
  // used with different language types based on configuration.
  return !empty($results[$provider_id]) ? clone($results[$provider_id]) : $results[$provider_id];
}

/**
 * Return the passed language provider weight or a default value.
 *
 * @param $provider
 *   A language provider data structure.
 *
 * @return
 *   A numeric weight.
 */
function language_provider_weight($provider) {
  $default = is_numeric($provider) ? $provider : 0;
  return isset($provider['weight']) && is_numeric($provider['weight']) ? $provider['weight'] : $default;
  // language negotiation 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];
}

/**
 * Default language provider.
 * Returns the default language code.
 *
 * @return
 *   The default language code.
+11 −11
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ function locale_language_from_session($languages) {
function locale_language_from_url($languages) {
  $language_url = FALSE;

  if (!language_negotiation_get_any(LANGUAGE_NEGOTIATION_URL)) {
  if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL)) {
    return $language_url;
  }

@@ -303,8 +303,8 @@ function locale_language_from_url($languages) {
 * Determines the language to be assigned to URLs when none is detected.
 *
 * The language negotiation process has a fallback chain that ends with the
 * default language provider. Each built-in language type has a separate
 * initialization:
 * default language negotiation method. Each built-in language type has a
 * separate initialization:
 * - Interface language, which is the only configurable one, always gets a valid
 *   value. If no request-specific language is detected, the default language
 *   will be used.
@@ -322,8 +322,8 @@ function locale_language_from_url($languages) {
 *
 * @param $languages
 *   (optional) An array of valid language objects. This is passed by
 *   language_provider_invoke() to every language provider callback, but it is
 *   not actually needed here. Defaults to NULL.
 *   language_negotiation_method_invoke() to every language method callback,
 *   but it is not actually needed here. Defaults to NULL.
 * @param $language_type
 *   (optional) The language type to fall back to. Defaults to the interface
 *   language.
@@ -406,7 +406,7 @@ function locale_language_switcher_session($type, $path) {
}

/**
 * Rewrite URLs for the URL language provider.
 * Rewrite URLs for the URL language negotiation method.
 */
function locale_language_url_rewrite_url(&$path, &$options) {
  static $drupal_static_fast;
@@ -492,7 +492,7 @@ function locale_language_negotiation_url_domains_save(array $domains) {
}

/**
 * Rewrite URLs for the Session language provider.
 * Rewrite URLs for the Session language negotiation method.
 */
function locale_language_url_rewrite_session(&$path, &$options) {
  static $query_rewrite, $query_param, $query_value;
@@ -506,16 +506,16 @@ function locale_language_url_rewrite_session(&$path, &$options) {
      $languages = language_list(TRUE);
      $query_param = check_plain(variable_get('locale_language_negotiation_session_param', 'language'));
      $query_value = isset($_GET[$query_param]) ? check_plain($_GET[$query_param]) : NULL;
      $query_rewrite = isset($languages[$query_value]) && language_negotiation_get_any(LANGUAGE_NEGOTIATION_SESSION);
      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION);
    }
    else {
      $query_rewrite = FALSE;
    }
  }

  // If the user is anonymous, the user language provider is enabled, and the
  // corresponding option has been set, we must preserve any explicit user
  // language preference even with cookies disabled.
  // If the user is anonymous, the user language negotiation method is enabled,
  // and the corresponding option has been set, we must preserve any explicit
  // user language preference even with cookies disabled.
  if ($query_rewrite) {
    if (is_string($options['query'])) {
      $options['query'] = drupal_get_query_array($options['query']);
+3 −3
Original line number Diff line number Diff line
@@ -140,9 +140,9 @@ function update_prepare_stored_includes() {
  // Update language negotiation settings.
  foreach (language_types_get_all() as $language_type) {
    $negotiation = variable_get("language_negotiation_$language_type", array());
    foreach ($negotiation as $id => &$provider) {
      if (isset($negotiation[$id]['file']) && $negotiation[$id]['file'] == 'includes/locale.inc') {
        $negotiation[$id]['file'] = 'core/includes/locale.inc';
    foreach ($negotiation as $method_id => &$method) {
      if (isset($method['file']) && $method['file'] == 'includes/locale.inc') {
        $method['file'] = 'core/includes/locale.inc';
      }
    }
    variable_set("language_negotiation_$language_type", $negotiation);
+50 −52

File changed.

Preview size limit exceeded, changes collapsed.

+30 −30
Original line number Diff line number Diff line
@@ -74,9 +74,9 @@ 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 provider identifiers to use to
 *     initialize this language. Defining this key makes the language type
 *     non-configurable and will always use the specified providers in the given
 *   - "fixed": A fixed array of language negotiation 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.
 */
function hook_language_types_info() {
@@ -86,7 +86,7 @@ function hook_language_types_info() {
      'description' => t('A custom language type.'),
    ),
    'fixed_custom_language_type' => array(
      'fixed' => array('custom_language_provider'),
      'fixed' => array('custom_language_negotiation_method'),
    ),
  );
}
@@ -106,59 +106,59 @@ function hook_language_types_info_alter(array &$language_types) {
}

/**
 * Allow modules to define their own language providers.
 * Allow modules to define their own language negotiation methods.
 *
 * @return
 *   An array of language provider definitions. Each language provider has an
 *   identifier key. The language provider definition is an associative array
 *   that may contain the following key-value pairs:
 *   - "types": An array of allowed language types. If a language provider does
 *     not specify which language types it should be used with, it will be
 *     available for all the configurable language types.
 *   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:
 *   - "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.
 *   - "callbacks": An array of functions that will be called to perform various
 *     tasks. Possible key-value pairs are:
 *     - "language": Required. The callback that will determine the language
 *     - "negotiation": Required. The callback that will determine the language
 *       value.
 *     - "switcher": The callback that will determine the language switch links
 *       associated to the current language provider.
 *     - "language_switch": The callback that will determine the language
 *       switch links associated to the current language method.
 *     - "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 provider has.
 *   - "weight": The default weight the language negotiation method has.
 *   - "name": A human-readable identifier.
 *   - "description": A description of the language provider.
 *   - "config": An internal path pointing to the language provider
 *   - "description": A description of the language negotiation method.
 *   - "config": An internal path pointing to the language negotiation method
 *     configuration page.
 *   - "cache": The value Drupal's page cache should be set to for the current
 *     language provider to be invoked.
 *     language negotiation method to be invoked.
 */
function hook_language_negotiation_info() {
  return array(
    'custom_language_provider' => array(
    'custom_language_negotiation_method' => array(
      'callbacks' => array(
        'language' => 'custom_language_provider_callback',
        'switcher' => 'custom_language_switcher_callback',
        'url_rewrite' => 'custom_language_url_rewrite_callback',
        'negotiation' => 'custom_negotiation_callback',
        'language_switch' => 'custom_language_switch_callback',
        'url_rewrite' => 'custom_url_rewrite_callback',
      ),
      'file' => drupal_get_path('module', 'custom') . '/custom.module',
      'weight' => -4,
      'types' => array('custom_language_type'),
      'name' => t('Custom language provider'),
      'description' => t('This is a custom language provider.'),
      'name' => t('Custom language negotiation method'),
      'description' => t('This is a custom language negotiation method.'),
      'cache' => 0,
    ),
  );
}

/**
 * Perform alterations on language providers.
 * Perform alterations on language negotiation methods.
 *
 * @param $language_providers
 *   Array of language provider definitions.
 * @param $negotiation_info
 *   Array of language negotiation method definitions.
 */
function hook_language_negotiation_info_alter(array &$language_providers) {
  if (isset($language_providers['custom_language_provider'])) {
    $language_providers['custom_language_provider']['config'] = 'admin/config/regional/language/detection/custom-language-provider';
function hook_language_negotiation_info_alter(array &$negotiation_info) {
  if (isset($negotiation_info['custom_language_method'])) {
    $negotiation_info['custom_language_method']['config'] = 'admin/config/regional/language/detection/custom-language-method';
  }
}

Loading