cms/drupal/modules/field/field.multilingual.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Functions implementing Field API multilingual support.
       
     6  */
       
     7 
       
     8 /**
       
     9  * @defgroup field_language Field Language API
       
    10  * @{
       
    11  * Handling of multilingual fields.
       
    12  *
       
    13  * Fields natively implement multilingual support, and all fields use the
       
    14  * following structure:
       
    15  * @code
       
    16  * $entity->{$field_name}[$langcode][$delta][$column_name]
       
    17  * @endcode
       
    18  * Every field can hold a single or multiple value for each language belonging
       
    19  * to the available languages set:
       
    20  * - For untranslatable fields this set only contains LANGUAGE_NONE.
       
    21  * - For translatable fields this set can contain any language code. By default
       
    22  *   it is the list returned by field_content_languages(), which contains all
       
    23  *   installed languages with the addition of LANGUAGE_NONE. This default can be
       
    24  *   altered by modules implementing hook_field_available_languages_alter().
       
    25  *
       
    26  * The available languages for a particular field are returned by
       
    27  * field_available_languages(). Whether a field is translatable is determined by
       
    28  * calling field_is_translatable(), which checks the $field['translatable']
       
    29  * property returned by field_info_field(), and whether there is at least one
       
    30  * translation handler available for the field. A translation handler is a
       
    31  * module registering itself via hook_entity_info() to handle field
       
    32  * translations.
       
    33  *
       
    34  * By default, _field_invoke() and _field_invoke_multiple() are processing a
       
    35  * field in all available languages, unless they are given a language
       
    36  * suggestion. Based on that suggestion, _field_language_suggestion() determines
       
    37  * the languages to act on.
       
    38  *
       
    39  * Most field_attach_*() functions act on all available languages, except for
       
    40  * the following:
       
    41  * - field_attach_form() only takes a single language code, specifying which
       
    42  *   language the field values will be submitted in.
       
    43  * - field_attach_view() requires the language the entity will be displayed in.
       
    44  *   Since it is unknown whether a field translation exists for the requested
       
    45  *   language, the translation handler is responsible for performing one of the
       
    46  *   following actions:
       
    47  *   - Ignore missing translations, i.e. do not show any field values for the
       
    48  *     requested language. For example, see locale_field_language_alter().
       
    49  *   - Provide a value in a different language as fallback. By default, the
       
    50  *     fallback logic is applied separately to each field to ensure that there
       
    51  *     is a value for each field to display.
       
    52  *   The field language fallback logic relies on the global language fallback
       
    53  *   configuration. Therefore, the displayed field values can be in the
       
    54  *   requested language, but may be different if no values for the requested
       
    55  *   language are available. The default language fallback rules inspect all the
       
    56  *   enabled languages ordered by their weight. This behavior can be altered or
       
    57  *   even disabled by modules implementing hook_field_language_alter(), making
       
    58  *   it possible to choose the first approach. The display language for each
       
    59  *   field is returned by field_language().
       
    60  *
       
    61  * See @link field Field API @endlink for information about the other parts of
       
    62  * the Field API.
       
    63  */
       
    64 
       
    65 /**
       
    66  * Implements hook_multilingual_settings_changed().
       
    67  */
       
    68 function field_multilingual_settings_changed() {
       
    69   field_info_cache_clear();
       
    70 }
       
    71 
       
    72 /**
       
    73  * Collects the available languages for the given entity type and field.
       
    74  *
       
    75  * If the given field has language support enabled, an array of available
       
    76  * languages will be returned, otherwise only LANGUAGE_NONE will be returned.
       
    77  * Since the default value for a 'translatable' entity property is FALSE, we
       
    78  * ensure that only entities that are able to handle translations actually get
       
    79  * translatable fields.
       
    80  *
       
    81  * @param $entity_type
       
    82  *   The type of the entity the field is attached to, e.g. 'node' or 'user'.
       
    83  * @param $field
       
    84  *   A field structure.
       
    85  *
       
    86  * @return
       
    87  *   An array of valid language codes.
       
    88  */
       
    89 function field_available_languages($entity_type, $field) {
       
    90   static $drupal_static_fast;
       
    91   if (!isset($drupal_static_fast)) {
       
    92     $drupal_static_fast['field_languages'] = &drupal_static(__FUNCTION__);
       
    93   }
       
    94   $field_languages = &$drupal_static_fast['field_languages'];
       
    95   $field_name = $field['field_name'];
       
    96 
       
    97   if (!isset($field_languages[$entity_type][$field_name])) {
       
    98     // If the field has language support enabled we retrieve an (alterable) list
       
    99     // of enabled languages, otherwise we return just LANGUAGE_NONE.
       
   100     if (field_is_translatable($entity_type, $field)) {
       
   101       $languages = field_content_languages();
       
   102       // Let other modules alter the available languages.
       
   103       $context = array('entity_type' => $entity_type, 'field' => $field);
       
   104       drupal_alter('field_available_languages', $languages, $context);
       
   105       $field_languages[$entity_type][$field_name] = $languages;
       
   106     }
       
   107     else {
       
   108       $field_languages[$entity_type][$field_name] = array(LANGUAGE_NONE);
       
   109     }
       
   110   }
       
   111 
       
   112   return $field_languages[$entity_type][$field_name];
       
   113 }
       
   114 
       
   115 /**
       
   116  * Process the given language suggestion based on the available languages.
       
   117  *
       
   118  * If a non-empty language suggestion is provided it must appear among the
       
   119  * available languages, otherwise it will be ignored.
       
   120  *
       
   121  * @param $available_languages
       
   122  *   An array of valid language codes.
       
   123  * @param $language_suggestion
       
   124  *   A language code or an array of language codes keyed by field name.
       
   125  * @param $field_name
       
   126  *   The name of the field being processed.
       
   127  *
       
   128  * @return
       
   129  *   An array of valid language codes.
       
   130  */
       
   131 function _field_language_suggestion($available_languages, $language_suggestion, $field_name) {
       
   132   // Handle possible language suggestions.
       
   133   if (!empty($language_suggestion)) {
       
   134     // We might have an array of language suggestions keyed by field name.
       
   135     if (is_array($language_suggestion) && isset($language_suggestion[$field_name])) {
       
   136       $language_suggestion = $language_suggestion[$field_name];
       
   137     }
       
   138 
       
   139     // If we have a language suggestion and the suggested language is available,
       
   140     // we return only it.
       
   141     if (in_array($language_suggestion, $available_languages)) {
       
   142       $available_languages = array($language_suggestion);
       
   143     }
       
   144   }
       
   145 
       
   146   return $available_languages;
       
   147 }
       
   148 
       
   149 /**
       
   150  * Returns available content languages.
       
   151  *
       
   152  * The languages that may be associated to fields include LANGUAGE_NONE.
       
   153  *
       
   154  * @return
       
   155  *   An array of language codes.
       
   156  */
       
   157 function field_content_languages() {
       
   158   return array_keys(language_list() + array(LANGUAGE_NONE => NULL));
       
   159 }
       
   160 
       
   161 /**
       
   162  * Checks whether a field has language support.
       
   163  *
       
   164  * A field has language support enabled if its 'translatable' property is set to
       
   165  * TRUE, and its entity type has at least one translation handler registered.
       
   166  *
       
   167  * @param $entity_type
       
   168  *   The type of the entity the field is attached to.
       
   169  * @param $field
       
   170  *   A field data structure.
       
   171  *
       
   172  * @return
       
   173  *   TRUE if the field can be translated.
       
   174  */
       
   175 function field_is_translatable($entity_type, $field) {
       
   176   return $field['translatable'] && field_has_translation_handler($entity_type);
       
   177 }
       
   178 
       
   179 /**
       
   180  * Checks if a module is registered as a translation handler for a given entity.
       
   181  *
       
   182  * If no handler is passed, simply check if there is any translation handler
       
   183  * enabled for the given entity type.
       
   184  *
       
   185  * @param $entity_type
       
   186  *   The type of the entity whose fields are to be translated.
       
   187  * @param $handler
       
   188  *   (optional) The name of the handler to be checked. Defaults to NULL.
       
   189  *
       
   190  * @return
       
   191  *   TRUE, if the given handler is allowed to manage field translations. If no
       
   192  *   handler is passed, TRUE means there is at least one registered translation
       
   193  *   handler.
       
   194  */
       
   195 function field_has_translation_handler($entity_type, $handler = NULL) {
       
   196   $entity_info = entity_get_info($entity_type);
       
   197 
       
   198   if (isset($handler)) {
       
   199     return !empty($entity_info['translation'][$handler]);
       
   200   }
       
   201   elseif (isset($entity_info['translation'])) {
       
   202     foreach ($entity_info['translation'] as $handler_info) {
       
   203       // The translation handler must use a non-empty data structure.
       
   204       if (!empty($handler_info)) {
       
   205         return TRUE;
       
   206       }
       
   207     }
       
   208   }
       
   209 
       
   210   return FALSE;
       
   211 }
       
   212 
       
   213 /**
       
   214  * Ensures that a given language code is valid.
       
   215  *
       
   216  * Checks whether the given language is one of the enabled languages. Otherwise,
       
   217  * it returns the current, global language; or the site's default language, if
       
   218  * the additional parameter $default is TRUE.
       
   219  *
       
   220  * @param $langcode
       
   221  *   The language code to validate.
       
   222  * @param $default
       
   223  *   Whether to return the default language code or the current language code in
       
   224  *   case $langcode is invalid.
       
   225  * @return
       
   226  *   A valid language code.
       
   227  */
       
   228 function field_valid_language($langcode, $default = TRUE) {
       
   229   $enabled_languages = field_content_languages();
       
   230   if (in_array($langcode, $enabled_languages)) {
       
   231     return $langcode;
       
   232   }
       
   233   global $language_content;
       
   234   return $default ? language_default('language') : $language_content->language;
       
   235 }
       
   236 
       
   237 /**
       
   238  * Returns the display language for the fields attached to the given entity.
       
   239  *
       
   240  * The actual language for each given field is determined based on the requested
       
   241  * language and the actual data available in the fields themselves.
       
   242  * If there is no registered translation handler for the given entity type, the
       
   243  * display language to be used is just LANGUAGE_NONE, as no other language code
       
   244  * is allowed by field_available_languages().
       
   245  * If translation handlers are found, we let modules provide alternative display
       
   246  * languages for fields not having the requested language available.
       
   247  * Core language fallback rules are provided by locale_field_language_fallback()
       
   248  * which is called by locale_field_language_alter().
       
   249  *
       
   250  * @param $entity_type
       
   251  *   The type of $entity.
       
   252  * @param $entity
       
   253  *   The entity to be displayed.
       
   254  * @param $field_name
       
   255  *   (optional) The name of the field to be displayed. Defaults to NULL. If
       
   256  *   no value is specified, the display languages for every field attached to
       
   257  *   the given entity will be returned.
       
   258  * @param $langcode
       
   259  *   (optional) The language code $entity has to be displayed in. Defaults to
       
   260  *   NULL. If no value is given the current language will be used.
       
   261  *
       
   262  * @return
       
   263  *   A language code if a field name is specified, an array of language codes
       
   264  *   keyed by field name otherwise.
       
   265  */
       
   266 function field_language($entity_type, $entity, $field_name = NULL, $langcode = NULL) {
       
   267   $display_languages = &drupal_static(__FUNCTION__, array());
       
   268   list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
       
   269   $langcode = field_valid_language($langcode, FALSE);
       
   270 
       
   271   if (!isset($display_languages[$entity_type][$id][$langcode])) {
       
   272     $display_language = array();
       
   273 
       
   274     // By default display language is set to LANGUAGE_NONE if the field
       
   275     // translation is not available. It is up to translation handlers to
       
   276     // implement language fallback rules.
       
   277     foreach (field_info_instances($entity_type, $bundle) as $instance) {
       
   278       $display_language[$instance['field_name']] = isset($entity->{$instance['field_name']}[$langcode]) ? $langcode : LANGUAGE_NONE;
       
   279     }
       
   280 
       
   281     if (field_has_translation_handler($entity_type)) {
       
   282       $context = array(
       
   283         'entity_type' => $entity_type,
       
   284         'entity' => $entity,
       
   285         'language' => $langcode,
       
   286       );
       
   287       drupal_alter('field_language', $display_language, $context);
       
   288     }
       
   289 
       
   290     $display_languages[$entity_type][$id][$langcode] = $display_language;
       
   291   }
       
   292 
       
   293   $display_language = $display_languages[$entity_type][$id][$langcode];
       
   294 
       
   295   // Single-field mode.
       
   296   if (isset($field_name)) {
       
   297     return isset($display_language[$field_name]) ? $display_language[$field_name] : FALSE;
       
   298   }
       
   299 
       
   300   return $display_language;
       
   301 }