Projet

Général

Profil

Paste
Télécharger (16,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / i18n / i18n_field / i18n_field.module @ 76df55b7

1
<?php
2

    
3
/**
4
 * @file
5
 * Internationalization (i18n) module - Field handling
6
 *
7
 * For string keys we use:
8
 * - field:[field_name]:[bundle]:property, when it is an instance property (linked to bundle)
9
 * - field:[field_name]:#property..., when it is a field property (that may have multiple values)
10
 */
11

    
12
/**
13
 * Implements hook_menu().
14
 */
15
function i18n_field_menu() {
16
  $items = array();
17

    
18
  // Ensure the following is not executed until field_bundles is working and
19
  // tables are updated. Needed to avoid errors on initial installation.
20
  if (!module_exists('field_ui') || defined('MAINTENANCE_MODE')) {
21
    return $items;
22
  }
23

    
24
  // Create tabs for all possible bundles. From field_ui_menu().
25
  foreach (entity_get_info() as $entity_type => $entity_info) {
26
    if ($entity_info['fieldable']) {
27
      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
28
        if (isset($bundle_info['admin'])) {
29
          // Extract path information from the bundle.
30
          $path = $bundle_info['admin']['path'];
31
          // Different bundles can appear on the same path (e.g. %node_type and
32
          // %comment_node_type). To allow field_ui_menu_load() to extract the
33
          // actual bundle object from the translated menu router path
34
          // arguments, we need to identify the argument position of the bundle
35
          // name string ('bundle argument') and pass that position to the menu
36
          // loader. The position needs to be casted into a string; otherwise it
37
          // would be replaced with the bundle name string.
38
          if (isset($bundle_info['admin']['bundle argument'])) {
39
            $bundle_arg = $bundle_info['admin']['bundle argument'];
40
            $bundle_pos = (string) $bundle_arg;
41
          }
42
          else {
43
            $bundle_arg = $bundle_name;
44
            $bundle_pos = '0';
45
          }
46
          // This is the position of the %field_ui_menu placeholder in the
47
          // items below.
48
          $field_position = count(explode('/', $path)) + 1;
49

    
50
          // Extract access information, providing defaults.
51
          $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
52
          $access += array(
53
            'access callback' => 'user_access',
54
            'access arguments' => array('administer site configuration'),
55
          );
56
          $items["$path/fields/%field_ui_menu/translate"] = array(
57
            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
58
            'title' => 'Translate',
59
            'page callback' => 'i18n_field_page_translate',
60
            'page arguments' => array($field_position),
61
            'file' => 'i18n_field.pages.inc',
62
            'type' => MENU_LOCAL_TASK,
63
          ) + $access;
64

    
65
          $items["$path/fields/%field_ui_menu/translate/%i18n_language"] = array(
66
            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
67
            'title' => 'Instance',
68
            'page callback' => 'i18n_field_page_translate',
69
            'page arguments' => array($field_position, $field_position + 2),
70
            'file' => 'i18n_field.pages.inc',
71
            'type' => MENU_CALLBACK,
72
          ) + $access;
73
        }
74
      }
75
    }
76
  }
77
  return $items;
78
}
79

    
80
/**
81
 * Implements hook_hook_info().
82
 */
83
function i18n_field_hook_info() {
84
  $hooks['i18n_field_info'] = array(
85
    'group' => 'i18n',
86
  );
87
  return $hooks;
88
}
89

    
90
/**
91
 * Implements hook_field_attach_form().
92
 *
93
 * After the form fields are built. Translate title and description for fields with multiple values.
94
 */
95
function i18n_field_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
96
  // Determine the list of instances to iterate on.
97
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
98
  $instances = field_info_instances($entity_type, $bundle);
99
  foreach ($instances as $field_name => $instance) {
100
    if (isset($form[$field_name])) {
101
      $langcode = $form[$field_name]['#language'];
102
      $field = &$form[$field_name];
103
      // Note: cardinality for unlimited fields is -1
104
      if (isset($field[$langcode]['#cardinality']) && $field[$langcode]['#cardinality'] != 1) {
105
        $translated = i18n_string_object_translate('field_instance', $instance);
106
        if (!empty($field[$langcode]['#title'])) {
107
          $field[$langcode]['#title'] = $translated['label'];
108
        }
109
        if (!empty($field[$langcode]['#description'])) {
110
          $field[$langcode]['#description'] = $translated['description'];
111
        }
112
      }
113
    }
114
  }
115
}
116

    
117
/**
118
 * Implements hook_field_formatter_info().
119
 */
120
function i18n_field_field_formatter_info() {
121
  $types = array();
122
  foreach (i18n_field_type_info() as $type => $info) {
123
    if (!empty($info['translate_options'])) {
124
      $types[] = $type;
125
    }
126
  }
127
  return array(
128
    'i18n_list_default' => array(
129
      'label' => t('Default translated'),
130
      'field types' => $types,
131
    ),
132
  );
133
}
134

    
135
/**
136
 * Implements hook_field_formatter_view().
137
 */
138
function i18n_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
139
  $element = array();
140

    
141
  switch ($display['type']) {
142
    case 'i18n_list_default':
143
      if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) {
144
        $allowed_values = $translate($field);
145
      }
146
      else {
147
        // Defaults to list_default behavior
148
        $allowed_values = list_allowed_values($field);
149
      }
150
      foreach ($items as $delta => $item) {
151
        if (isset($allowed_values[$item['value']])) {
152
          $output = field_filter_xss($allowed_values[$item['value']]);
153
        }
154
        else {
155
          // If no match was found in allowed values, fall back to the key.
156
          $output = field_filter_xss($item['value']);
157
        }
158
        $element[$delta] = array('#markup' => $output);
159
      }
160
      break;
161
  }
162

    
163
  return $element;
164
}
165

    
166
/**
167
 * Implements hook_field_widget_form_alter().
168
 *
169
 * Translate:
170
 * - Title (label)
171
 * - Description (help)
172
 * - Default value
173
 * - List options
174
 */
175
function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
176
  global $language;
177

    
178
  // Don't translate if the widget is being shown on the field edit form.
179
  if ($form_state['build_info']['form_id'] == 'field_ui_field_edit_form') {
180
    return;
181
  }
182

    
183
  // Skip if we are missing any of the parameters
184
  if (empty($context['field']) || empty($context['instance']) || empty($context['langcode'])) {
185
    return;
186
  }
187
  $field = $context['field'];
188
  $instance = $context['instance'];
189
  $langcode = $context['langcode'];
190

    
191
  // Get the element to alter. Account for inconsistencies in how the element
192
  // is built for different field types.
193
  if (isset($element[0]) && count($element) == 1) {
194
    // Single-value file fields and image fields.
195
    $alter_element = &$element[0];
196
  }
197
  elseif (isset($element['value'])) {
198
    // Number fields. Single-value text fields.
199
    $alter_element = &$element['value'];
200
  }
201
  elseif ($field['type'] == 'entityreference' && isset($element['target_id'])) {
202
    // Entityreference fields using the entityreference_autocomplete widget.
203
    $alter_element = &$element['target_id'];
204
  }
205
  else {
206
    // All other fields.
207
    $alter_element = &$element;
208
  }
209

    
210
  // If a subelement has the same title as the parent, translate it instead.
211
  // Allows fields such as email and commerce_price to be translated.
212
  foreach (element_get_visible_children($element) as $key) {
213
    $single_value = ($field['cardinality'] == 1);
214
    $has_title = (isset($element['#title']) && isset($element[$key]['#title']));
215
    if ($single_value && $has_title && $element[$key]['#title'] == $element['#title']) {
216
      $alter_element = &$element[$key];
217
      break;
218
    }
219
  }
220

    
221
  // The field language may affect some variables (default) but not others (description will be in current page language)
222
  $i18n_langcode = empty($alter_element['#language']) || $alter_element['#language'] == LANGUAGE_NONE ? $language->language : $alter_element['#language'];
223

    
224
  // Translate instance to current page language and set to form_state
225
  // so it will be used for validation messages later.
226
  $instance_current = i18n_string_object_translate('field_instance', $instance);
227
  if (isset($form_state['field'][$instance['field_name']][$langcode]['instance'])) {
228
    $form_state['field'][$instance['field_name']][$langcode]['instance'] = $instance_current;
229
  }
230

    
231
  // Translate field title if set and it is the default one.
232
  if (!empty($instance_current['label']) && $instance_current['label'] != $instance['label']) {
233
    if (!empty($alter_element['#title']) && $alter_element['#title'] == check_plain($instance['label'])) {
234
      $alter_element['#title'] = check_plain($instance_current['label']);
235
    }
236
  }
237

    
238
  // Translate field description if set and it is the default one.
239
  if (!empty($instance_current['description']) && $instance_current['description'] != $instance['description']) {
240
    if (!empty($alter_element['#description'])) {
241
      // Allow single-value file fields and image fields to have their
242
      // descriptions translated. file_field_widget_form() passes the
243
      // description through theme('file_upload_help'), so i18n_field
244
      // must do the same.
245
      $filefield = in_array($field['type'], array('file', 'image'));
246
      $single_value = ($field['cardinality'] == 1);
247
      $no_default = empty($alter_element['#default_value']['fid']);
248
      if ($filefield && $single_value && $no_default) {
249
        $help_variables = array(
250
          'description' => field_filter_xss($instance['description']),
251
          'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
252
        );
253
        $original_description = theme('file_upload_help', $help_variables);
254
        if ($alter_element['#description'] == $original_description) {
255
          $help_variables = array(
256
            'description' => field_filter_xss($instance_current['description']),
257
            'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
258
          );
259
          $alter_element['#description'] = theme('file_upload_help', $help_variables);
260
        }
261
      }
262
      elseif ($alter_element['#description'] == field_filter_xss($instance['description'])) {
263
        $alter_element['#description'] = field_filter_xss($instance_current['description']);
264
      }
265
    }
266
  }
267

    
268
  // Translate list options.
269
  $has_options = (!empty($alter_element['#options']) || $field['type'] == 'list_boolean');
270
  $has_allowed_values = !empty($field['settings']['allowed_values']);
271
  $translate = i18n_field_type_info($field['type'], 'translate_options');
272
  if ($has_options && $has_allowed_values && $translate) {
273
    $alter_element['#options'] = $translate($field, $i18n_langcode);
274
    if (isset($alter_element['#properties']) && !empty($alter_element['#properties']['empty_option'])) {
275
      $label = theme('options_none', array('instance' => $instance, 'option' => $alter_element['#properties']['empty_option']));
276
      $alter_element['#options'] = array('_none' => $label) + $alter_element['#options'];
277
    }
278
    // Translate list_boolean fields using the checkboxes widget.
279
    if (!empty($alter_element['#title']) && $field['type'] == 'list_boolean' && !empty($alter_element['#on_value'])) {
280
      $on_value = $alter_element['#on_value'];
281
      $alter_element['#options'];
282
      $alter_element['#title'] = $alter_element['#options'][$on_value];
283
      // For using label instead of "On value".
284
      if ($instance['widget']['settings']['display_label']) {
285
        $alter_element['#title'] = $instance_current['label'];
286
      }
287
    }
288
  }
289

    
290
  // Check for more parameters, skip this part if missing.
291
  if (!isset($context['delta']) || !isset($context['items'])) {
292
    return;
293
  }
294
  $delta = $context['delta'];
295
  $items = $context['items'];
296

    
297
  // Translate default value.
298
  $has_default_value = (isset($alter_element['#default_value']) && !empty($instance['default_value'][$delta]['value']));
299
  $storage_has_value = !empty($items[$delta]['value']);
300
  $translate = i18n_field_type_info($field['type'], 'translate_default');
301
  if ($has_default_value && $storage_has_value && $translate) {
302
    // Compare the default value with the value currently in storage.
303
    if ($instance['default_value'][$delta]['value'] === $items[$delta]['value']) {
304
      $alter_element['#default_value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
305
    }
306
  }
307
}
308

    
309
/**
310
 * Implements hook_field_attach_view_alter().
311
 */
312
function i18n_field_field_attach_view_alter(&$output, $context) {
313
  foreach (element_children($output) as $field_name) {
314
    $element = &$output[$field_name];
315
    if (!empty($element['#entity_type']) && !empty($element['#field_name']) && !empty($element['#bundle'])) {
316
      $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
317

    
318
      // Translate field title if set
319
      if (!empty($instance['label'])) {
320
        $element['#title'] = i18n_field_translate_property($instance, 'label');
321
      }
322

    
323
      // Translate field description if set
324
      if (!empty($instance['description'])) {
325
        $element['#description'] = i18n_field_translate_property($instance, 'description');
326
      }
327
    }
328
  }
329
}
330

    
331
/**
332
 * Implements hook_field_create_field().
333
 */
334
function i18n_field_field_create_field($field) {
335
  i18n_field_field_update_strings($field);
336
}
337

    
338
/**
339
 * Implements hook_field_create_instance().
340
 */
341
function i18n_field_field_create_instance($instance) {
342
  i18n_field_instance_update_strings($instance);
343
}
344

    
345
/**
346
 * Implements hook_field_delete_instance().
347
 */
348
function i18n_field_field_delete_instance($instance) {
349
  i18n_string_object_remove('field_instance', $instance);
350
}
351

    
352
/**
353
 * Implements hook_field_update_instance().
354
 */
355
function i18n_field_field_update_instance($instance, $prior_instance) {
356
  i18n_field_instance_update_strings($instance);
357
}
358

    
359
/**
360
 * Implements hook_field_update_field().
361
 */
362
function i18n_field_field_update_field($field) {
363
  i18n_field_field_update_strings($field);
364
}
365

    
366
/**
367
 * Update field strings
368
 */
369
function i18n_field_field_update_strings($field) {
370
  i18n_string_object_update('field', $field);
371
}
372

    
373
/**
374
 * Update field instance strings
375
 */
376
function i18n_field_instance_update_strings($instance) {
377
  i18n_string_object_update('field_instance', $instance);
378
}
379

    
380
/**
381
 * Returns the array of translated allowed values for a list field.
382
 *
383
 * The strings are not safe for output. Keys and values of the array should be
384
 * sanitized through field_filter_xss() before being displayed.
385
 *
386
 * @param $field
387
 *   The field definition.
388
 *
389
 * @return
390
 *   The array of allowed values. Keys of the array are the raw stored values
391
 *   (number or text), values of the array are the display labels.
392
 */
393
function i18n_field_translate_allowed_values($field, $langcode = NULL) {
394
  if (!empty($field['settings']['allowed_values'])) {
395
    return i18n_string_translate(array('field', $field['field_name'], '#allowed_values'), $field['settings']['allowed_values'], array('langcode' => $langcode, 'sanitize' => FALSE));
396
  }
397
  else {
398
    return array();
399
  }
400
}
401

    
402
/**
403
 * Translate field default.
404
 */
405
function i18n_field_translate_default($instance, $value, $langcode = NULL) {
406
  // The default value does not need sanitizing in a text_textfield widget.
407
  $sanitize = !($instance['widget']['type'] == 'text_textfield' && $instance['widget']['module'] == 'text');
408
  return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode, 'sanitize' => $sanitize));
409
}
410

    
411
/**
412
 * Translate field property
413
 */
414
function i18n_field_translate_property($instance, $property, $langcode = NULL) {
415
  // For performance reasons, we translate the whole instance once, which is cached.
416
  $instance = i18n_string_object_translate('field_instance', $instance, array('langcode' => $langcode));
417
  return $instance[$property];
418
}
419

    
420
/**
421
 * Get i18n information for translating fields.
422
 *
423
 * @param $type
424
 *   Optional field type.
425
 * @param $property
426
 *   Optional property to get from field type.
427
 *
428
 * @return
429
 *   - The property for the field if $type and $property set.
430
 *   - Array of properties for the field type if only $type is set.
431
 *   - Array of translation information for all field types.
432
 */
433
function i18n_field_type_info($type = NULL, $property = NULL) {
434
  $info = &drupal_static(__FUNCTION__);
435
  if (!isset($info)) {
436
    $info = module_invoke_all('i18n_field_info');
437
    drupal_alter('i18n_field_info', $info);
438
  }
439
  if ($property) {
440
    return isset($info[$type]) && isset($info[$type][$property]) ? $info[$type][$property] : NULL;
441
  }
442
  elseif ($type) {
443
    return isset($info[$type]) ? $info[$type] : array();
444
  }
445
  else {
446
    return $info;
447
  }
448
}