| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 | <?php/** * @file * Functions implementing Field API multilingual support. *//** * @defgroup field_language Field Language API * @{ * Handling of multilingual fields. * * Fields natively implement multilingual support, and all fields use the * following structure: * @code * $entity->{$field_name}[$langcode][$delta][$column_name] * @endcode * Every field can hold a single or multiple value for each language belonging * to the available languages set: * - For untranslatable fields this set only contains LANGUAGE_NONE. * - For translatable fields this set can contain any language code. By default *   it is the list returned by field_content_languages(), which contains all *   installed languages with the addition of LANGUAGE_NONE. This default can be *   altered by modules implementing hook_field_available_languages_alter(). * * The available languages for a particular field are returned by * field_available_languages(). Whether a field is translatable is determined by * calling field_is_translatable(), which checks the $field['translatable'] * property returned by field_info_field(), and whether there is at least one * translation handler available for the field. A translation handler is a * module registering itself via hook_entity_info() to handle field * translations. * * By default, _field_invoke() and _field_invoke_multiple() are processing a * field in all available languages, unless they are given a language * suggestion. Based on that suggestion, _field_language_suggestion() determines * the languages to act on. * * Most field_attach_*() functions act on all available languages, except for * the following: * - field_attach_form() only takes a single language code, specifying which *   language the field values will be submitted in. * - field_attach_view() requires the language the entity will be displayed in. *   Since it is unknown whether a field translation exists for the requested *   language, the translation handler is responsible for performing one of the *   following actions: *   - Ignore missing translations, i.e. do not show any field values for the *     requested language. For example, see locale_field_language_alter(). *   - Provide a value in a different language as fallback. By default, the *     fallback logic is applied separately to each field to ensure that there *     is a value for each field to display. *   The field language fallback logic relies on the global language fallback *   configuration. Therefore, the displayed field values can be in the *   requested language, but may be different if no values for the requested *   language are available. The default language fallback rules inspect all the *   enabled languages ordered by their weight. This behavior can be altered or *   even disabled by modules implementing hook_field_language_alter(), making *   it possible to choose the first approach. The display language for each *   field is returned by field_language(). * * See @link field Field API @endlink for information about the other parts of * the Field API. *//** * Implements hook_multilingual_settings_changed(). */function field_multilingual_settings_changed() {  field_info_cache_clear();}/** * Collects the available languages for the given entity type and field. * * If the given field has language support enabled, an array of available * languages will be returned, otherwise only LANGUAGE_NONE will be returned. * Since the default value for a 'translatable' entity property is FALSE, we * ensure that only entities that are able to handle translations actually get * translatable fields. * * @param $entity_type *   The type of the entity the field is attached to, e.g. 'node' or 'user'. * @param $field *   A field structure. * * @return *   An array of valid language codes. */function field_available_languages($entity_type, $field) {  static $drupal_static_fast;  if (!isset($drupal_static_fast)) {    $drupal_static_fast['field_languages'] = &drupal_static(__FUNCTION__);  }  $field_languages = &$drupal_static_fast['field_languages'];  $field_name = $field['field_name'];  if (!isset($field_languages[$entity_type][$field_name])) {    // If the field has language support enabled we retrieve an (alterable) list    // of enabled languages, otherwise we return just LANGUAGE_NONE.    if (field_is_translatable($entity_type, $field)) {      $languages = field_content_languages();      // Let other modules alter the available languages.      $context = array('entity_type' => $entity_type, 'field' => $field);      drupal_alter('field_available_languages', $languages, $context);      $field_languages[$entity_type][$field_name] = $languages;    }    else {      $field_languages[$entity_type][$field_name] = array(LANGUAGE_NONE);    }  }  return $field_languages[$entity_type][$field_name];}/** * Process the given language suggestion based on the available languages. * * If a non-empty language suggestion is provided it must appear among the * available languages, otherwise it will be ignored. * * @param $available_languages *   An array of valid language codes. * @param $language_suggestion *   A language code or an array of language codes keyed by field name. * @param $field_name *   The name of the field being processed. * * @return *   An array of valid language codes. */function _field_language_suggestion($available_languages, $language_suggestion, $field_name) {  // Handle possible language suggestions.  if (!empty($language_suggestion)) {    // We might have an array of language suggestions keyed by field name.    if (is_array($language_suggestion) && isset($language_suggestion[$field_name])) {      $language_suggestion = $language_suggestion[$field_name];    }    // If we have a language suggestion and the suggested language is available,    // we return only it.    if (in_array($language_suggestion, $available_languages)) {      $available_languages = array($language_suggestion);    }  }  return $available_languages;}/** * Returns available content languages. * * The languages that may be associated to fields include LANGUAGE_NONE. * * @return *   An array of language codes. */function field_content_languages() {  return array_keys(language_list() + array(LANGUAGE_NONE => NULL));}/** * Checks whether a field has language support. * * A field has language support enabled if its 'translatable' property is set to * TRUE, and its entity type has at least one translation handler registered. * * @param $entity_type *   The type of the entity the field is attached to. * @param $field *   A field data structure. * * @return *   TRUE if the field can be translated. */function field_is_translatable($entity_type, $field) {  return $field['translatable'] && field_has_translation_handler($entity_type);}/** * Checks if a module is registered as a translation handler for a given entity. * * If no handler is passed, simply check if there is any translation handler * enabled for the given entity type. * * @param $entity_type *   The type of the entity whose fields are to be translated. * @param $handler *   (optional) The name of the handler to be checked. Defaults to NULL. * * @return *   TRUE, if the given handler is allowed to manage field translations. If no *   handler is passed, TRUE means there is at least one registered translation *   handler. */function field_has_translation_handler($entity_type, $handler = NULL) {  $entity_info = entity_get_info($entity_type);  if (isset($handler)) {    return !empty($entity_info['translation'][$handler]);  }  elseif (isset($entity_info['translation'])) {    foreach ($entity_info['translation'] as $handler_info) {      // The translation handler must use a non-empty data structure.      if (!empty($handler_info)) {        return TRUE;      }    }  }  return FALSE;}/** * Ensures that a given language code is valid. * * Checks whether the given language is one of the enabled languages. Otherwise, * it returns the current, global language; or the site's default language, if * the additional parameter $default is TRUE. * * @param $langcode *   The language code to validate. * @param $default *   Whether to return the default language code or the current language code in *   case $langcode is invalid. * @return *   A valid language code. */function field_valid_language($langcode, $default = TRUE) {  $enabled_languages = field_content_languages();  if (in_array($langcode, $enabled_languages)) {    return $langcode;  }  global $language_content;  return $default ? language_default('language') : $language_content->language;}/** * Returns the display language for the fields attached to the given entity. * * The actual language for each given field is determined based on the requested * language and the actual data available in the fields themselves. * If there is no registered translation handler for the given entity type, the * display language to be used is just LANGUAGE_NONE, as no other language code * is allowed by field_available_languages(). * If translation handlers are found, we let modules provide alternative display * languages for fields not having the requested language available. * Core language fallback rules are provided by locale_field_language_fallback() * which is called by locale_field_language_alter(). * * @param $entity_type *   The type of $entity. * @param $entity *   The entity to be displayed. * @param $field_name *   (optional) The name of the field to be displayed. Defaults to NULL. If *   no value is specified, the display languages for every field attached to *   the given entity will be returned. * @param $langcode *   (optional) The language code $entity has to be displayed in. Defaults to *   NULL. If no value is given the current language will be used. * * @return *   A language code if a field name is specified, an array of language codes *   keyed by field name otherwise. */function field_language($entity_type, $entity, $field_name = NULL, $langcode = NULL) {  $display_languages = &drupal_static(__FUNCTION__, array());  list($id, , $bundle) = entity_extract_ids($entity_type, $entity);  $langcode = field_valid_language($langcode, FALSE);  if (!isset($display_languages[$entity_type][$id][$langcode])) {    $display_language = array();    // By default display language is set to LANGUAGE_NONE if the field    // translation is not available. It is up to translation handlers to    // implement language fallback rules.    foreach (field_info_instances($entity_type, $bundle) as $instance) {      $display_language[$instance['field_name']] = isset($entity->{$instance['field_name']}[$langcode]) ? $langcode : LANGUAGE_NONE;    }    if (field_has_translation_handler($entity_type)) {      $context = array(        'entity_type' => $entity_type,        'entity' => $entity,        'language' => $langcode,      );      drupal_alter('field_language', $display_language, $context);    }    $display_languages[$entity_type][$id][$langcode] = $display_language;  }  $display_language = $display_languages[$entity_type][$id][$langcode];  // Single-field mode.  if (isset($field_name)) {    return isset($display_language[$field_name]) ? $display_language[$field_name] : FALSE;  }  return $display_language;}
 |