123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- <?php
- /**
- * @file
- * Internationalization (i18n) module - Field handling
- *
- * For string keys we use:
- * - field:[field_name]:[bundle]:property, when it is an instance property (linked to bundle)
- * - field:[field_name]:#property..., when it is a field property (that may have multiple values)
- */
- /**
- * Implements hook_menu().
- */
- function i18n_field_menu() {
- $items = array();
- // Ensure the following is not executed until field_bundles is working and
- // tables are updated. Needed to avoid errors on initial installation.
- if (!module_exists('field_ui') || defined('MAINTENANCE_MODE')) {
- return $items;
- }
- // Create tabs for all possible bundles. From field_ui_menu().
- foreach (entity_get_info() as $entity_type => $entity_info) {
- if ($entity_info['fieldable']) {
- foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
- if (isset($bundle_info['admin'])) {
- // Extract path information from the bundle.
- $path = $bundle_info['admin']['path'];
- // Different bundles can appear on the same path (e.g. %node_type and
- // %comment_node_type). To allow field_ui_menu_load() to extract the
- // actual bundle object from the translated menu router path
- // arguments, we need to identify the argument position of the bundle
- // name string ('bundle argument') and pass that position to the menu
- // loader. The position needs to be casted into a string; otherwise it
- // would be replaced with the bundle name string.
- if (isset($bundle_info['admin']['bundle argument'])) {
- $bundle_arg = $bundle_info['admin']['bundle argument'];
- $bundle_pos = (string) $bundle_arg;
- }
- else {
- $bundle_arg = $bundle_name;
- $bundle_pos = '0';
- }
- // This is the position of the %field_ui_menu placeholder in the
- // items below.
- $field_position = count(explode('/', $path)) + 1;
- // Extract access information, providing defaults.
- $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
- $access += array(
- 'access callback' => 'user_access',
- 'access arguments' => array('administer site configuration'),
- );
- $items["$path/fields/%field_ui_menu/translate"] = array(
- 'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
- 'title' => 'Translate',
- 'page callback' => 'i18n_field_page_translate',
- 'page arguments' => array($field_position),
- 'file' => 'i18n_field.pages.inc',
- 'type' => MENU_LOCAL_TASK,
- ) + $access;
- $items["$path/fields/%field_ui_menu/translate/%i18n_language"] = array(
- 'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
- 'title' => 'Instance',
- 'page callback' => 'i18n_field_page_translate',
- 'page arguments' => array($field_position, $field_position + 2),
- 'file' => 'i18n_field.pages.inc',
- 'type' => MENU_CALLBACK,
- ) + $access;
- }
- }
- }
- }
- return $items;
- }
- /**
- * Implements hook_hook_info().
- */
- function i18n_field_hook_info() {
- $hooks['i18n_field_info'] = array(
- 'group' => 'i18n',
- );
- return $hooks;
- }
- /**
- * Implements hook_field_attach_form().
- *
- * After the form fields are built. Translate title and description for fields with multiple values.
- */
- function i18n_field_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
- // Determine the list of instances to iterate on.
- list(, , $bundle) = entity_extract_ids($entity_type, $entity);
- $instances = field_info_instances($entity_type, $bundle);
- foreach ($instances as $field_name => $instance) {
- if (isset($form[$field_name])) {
- $langcode = $form[$field_name]['#language'];
- $field = &$form[$field_name];
- // Note: cardinality for unlimited fields is -1
- if (isset($field[$langcode]['#cardinality']) && $field[$langcode]['#cardinality'] != 1) {
- $translated = i18n_string_object_translate('field_instance', $instance);
- if (!empty($field[$langcode]['#title'])) {
- $field[$langcode]['#title'] = $translated['label'];
- }
- if (!empty($field[$langcode]['#description'])) {
- $field[$langcode]['#description'] = $translated['description'];
- }
- }
- }
- }
- }
- /**
- * Implements hook_field_formatter_info().
- */
- function i18n_field_field_formatter_info() {
- $types = array();
- foreach (i18n_field_type_info() as $type => $info) {
- if (!empty($info['translate_options'])) {
- $types[] = $type;
- }
- }
- return array(
- 'i18n_list_default' => array(
- 'label' => t('Default translated'),
- 'field types' => $types,
- ),
- );
- }
- /**
- * Implements hook_field_formatter_view().
- */
- function i18n_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
- $element = array();
- switch ($display['type']) {
- case 'i18n_list_default':
- if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) {
- $allowed_values = $translate($field);
- }
- else {
- // Defaults to list_default behavior
- $allowed_values = list_allowed_values($field);
- }
- foreach ($items as $delta => $item) {
- if (isset($allowed_values[$item['value']])) {
- $output = field_filter_xss($allowed_values[$item['value']]);
- }
- else {
- // If no match was found in allowed values, fall back to the key.
- $output = field_filter_xss($item['value']);
- }
- $element[$delta] = array('#markup' => $output);
- }
- break;
- }
- return $element;
- }
- /**
- * Implements hook_field_widget_form_alter().
- *
- * Translate:
- * - Title (label)
- * - Description (help)
- * - Default value
- * - List options
- */
- function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
- global $language;
- // Don't translate if the widget is being shown on the field edit form.
- if ($form_state['build_info']['form_id'] == 'field_ui_field_edit_form') {
- return;
- }
- // Skip if we are missing any of the parameters
- if (empty($context['field']) || empty($context['instance']) || empty($context['langcode'])) {
- return;
- }
- $field = $context['field'];
- $instance = $context['instance'];
- $langcode = $context['langcode'];
- // Get the element to alter. Account for inconsistencies in how the element
- // is built for different field types.
- if (isset($element[0]) && count($element) == 1) {
- // Single-value file fields and image fields.
- $alter_element = &$element[0];
- }
- elseif (isset($element['value'])) {
- // Number fields. Single-value text fields.
- $alter_element = &$element['value'];
- }
- elseif ($field['type'] == 'entityreference' && isset($element['target_id'])) {
- // Entityreference fields using the entityreference_autocomplete widget.
- $alter_element = &$element['target_id'];
- }
- else {
- // All other fields.
- $alter_element = &$element;
- }
- // If a subelement has the same title as the parent, translate it instead.
- // Allows fields such as email and commerce_price to be translated.
- foreach (element_get_visible_children($element) as $key) {
- $single_value = ($field['cardinality'] == 1);
- $has_title = (isset($element['#title']) && isset($element[$key]['#title']));
- if ($single_value && $has_title && $element[$key]['#title'] == $element['#title']) {
- $alter_element = &$element[$key];
- break;
- }
- }
- // The field language may affect some variables (default) but not others (description will be in current page language)
- $i18n_langcode = empty($alter_element['#language']) || $alter_element['#language'] == LANGUAGE_NONE ? $language->language : $alter_element['#language'];
- // Translate instance to current page language and set to form_state
- // so it will be used for validation messages later.
- $instance_current = i18n_string_object_translate('field_instance', $instance);
- if (isset($form_state['field'][$instance['field_name']][$langcode]['instance'])) {
- $form_state['field'][$instance['field_name']][$langcode]['instance'] = $instance_current;
- }
- // Translate field title if set and it is the default one.
- if (!empty($instance_current['label']) && $instance_current['label'] != $instance['label']) {
- if (!empty($alter_element['#title']) && $alter_element['#title'] == check_plain($instance['label'])) {
- $alter_element['#title'] = check_plain($instance_current['label']);
- }
- }
- // Translate field description if set and it is the default one.
- if (!empty($instance_current['description']) && $instance_current['description'] != $instance['description']) {
- if (!empty($alter_element['#description'])) {
- // Allow single-value file fields and image fields to have their
- // descriptions translated. file_field_widget_form() passes the
- // description through theme('file_upload_help'), so i18n_field
- // must do the same.
- $filefield = in_array($field['type'], array('file', 'image'));
- $single_value = ($field['cardinality'] == 1);
- $no_default = empty($alter_element['#default_value']['fid']);
- if ($filefield && $single_value && $no_default) {
- $help_variables = array(
- 'description' => field_filter_xss($instance['description']),
- 'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
- );
- $original_description = theme('file_upload_help', $help_variables);
- if ($alter_element['#description'] == $original_description) {
- $help_variables = array(
- 'description' => field_filter_xss($instance_current['description']),
- 'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
- );
- $alter_element['#description'] = theme('file_upload_help', $help_variables);
- }
- }
- elseif ($alter_element['#description'] == field_filter_xss($instance['description'])) {
- $alter_element['#description'] = field_filter_xss($instance_current['description']);
- }
- }
- }
- // Translate list options.
- $has_options = (!empty($alter_element['#options']) || $field['type'] == 'list_boolean');
- $has_allowed_values = !empty($field['settings']['allowed_values']);
- $translate = i18n_field_type_info($field['type'], 'translate_options');
- if ($has_options && $has_allowed_values && $translate) {
- $alter_element['#options'] = $translate($field, $i18n_langcode);
- if (isset($alter_element['#properties']) && !empty($alter_element['#properties']['empty_option'])) {
- $label = theme('options_none', array('instance' => $instance, 'option' => $alter_element['#properties']['empty_option']));
- $alter_element['#options'] = array('_none' => $label) + $alter_element['#options'];
- }
- // Translate list_boolean fields using the checkboxes widget.
- if (!empty($alter_element['#title']) && $field['type'] == 'list_boolean' && !empty($alter_element['#on_value'])) {
- $on_value = $alter_element['#on_value'];
- $alter_element['#options'];
- $alter_element['#title'] = $alter_element['#options'][$on_value];
- // For using label instead of "On value".
- if ($instance['widget']['settings']['display_label']) {
- $alter_element['#title'] = $instance_current['label'];
- }
- }
- }
- // Check for more parameters, skip this part if missing.
- if (!isset($context['delta']) || !isset($context['items'])) {
- return;
- }
- $delta = $context['delta'];
- $items = $context['items'];
- // Translate default value.
- $has_default_value = (isset($alter_element['#default_value']) && !empty($instance['default_value'][$delta]['value']));
- $storage_has_value = !empty($items[$delta]['value']);
- $translate = i18n_field_type_info($field['type'], 'translate_default');
- if ($has_default_value && $storage_has_value && $translate) {
- // Compare the default value with the value currently in storage.
- if ($instance['default_value'][$delta]['value'] === $items[$delta]['value']) {
- $alter_element['#default_value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
- }
- }
- }
- /**
- * Implements hook_field_attach_view_alter().
- */
- function i18n_field_field_attach_view_alter(&$output, $context) {
- foreach (element_children($output) as $field_name) {
- $element = &$output[$field_name];
- if (!empty($element['#entity_type']) && !empty($element['#field_name']) && !empty($element['#bundle'])) {
- $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
- // Translate field title if set
- if (!empty($instance['label'])) {
- $element['#title'] = i18n_field_translate_property($instance, 'label');
- }
- // Translate field description if set
- if (!empty($instance['description'])) {
- $element['#description'] = i18n_field_translate_property($instance, 'description');
- }
- }
- }
- }
- /**
- * Implements hook_field_create_field().
- */
- function i18n_field_field_create_field($field) {
- i18n_field_field_update_strings($field);
- }
- /**
- * Implements hook_field_create_instance().
- */
- function i18n_field_field_create_instance($instance) {
- i18n_field_instance_update_strings($instance);
- }
- /**
- * Implements hook_field_delete_instance().
- */
- function i18n_field_field_delete_instance($instance) {
- i18n_string_object_remove('field_instance', $instance);
- }
- /**
- * Implements hook_field_update_instance().
- */
- function i18n_field_field_update_instance($instance, $prior_instance) {
- i18n_field_instance_update_strings($instance);
- }
- /**
- * Implements hook_field_update_field().
- */
- function i18n_field_field_update_field($field) {
- i18n_field_field_update_strings($field);
- }
- /**
- * Update field strings
- */
- function i18n_field_field_update_strings($field) {
- i18n_string_object_update('field', $field);
- }
- /**
- * Update field instance strings
- */
- function i18n_field_instance_update_strings($instance) {
- i18n_string_object_update('field_instance', $instance);
- }
- /**
- * Returns the array of translated allowed values for a list field.
- *
- * The strings are not safe for output. Keys and values of the array should be
- * sanitized through field_filter_xss() before being displayed.
- *
- * @param $field
- * The field definition.
- *
- * @return
- * The array of allowed values. Keys of the array are the raw stored values
- * (number or text), values of the array are the display labels.
- */
- function i18n_field_translate_allowed_values($field, $langcode = NULL) {
- if (!empty($field['settings']['allowed_values'])) {
- return i18n_string_translate(array('field', $field['field_name'], '#allowed_values'), $field['settings']['allowed_values'], array('langcode' => $langcode, 'sanitize' => FALSE));
- }
- else {
- return array();
- }
- }
- /**
- * Translate field default.
- */
- function i18n_field_translate_default($instance, $value, $langcode = NULL) {
- // The default value does not need sanitizing in a text_textfield widget.
- $sanitize = !($instance['widget']['type'] == 'text_textfield' && $instance['widget']['module'] == 'text');
- return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode, 'sanitize' => $sanitize));
- }
- /**
- * Translate field property
- */
- function i18n_field_translate_property($instance, $property, $langcode = NULL) {
- // For performance reasons, we translate the whole instance once, which is cached.
- $instance = i18n_string_object_translate('field_instance', $instance, array('langcode' => $langcode));
- return $instance[$property];
- }
- /**
- * Get i18n information for translating fields.
- *
- * @param $type
- * Optional field type.
- * @param $property
- * Optional property to get from field type.
- *
- * @return
- * - The property for the field if $type and $property set.
- * - Array of properties for the field type if only $type is set.
- * - Array of translation information for all field types.
- */
- function i18n_field_type_info($type = NULL, $property = NULL) {
- $info = &drupal_static(__FUNCTION__);
- if (!isset($info)) {
- $info = module_invoke_all('i18n_field_info');
- drupal_alter('i18n_field_info', $info);
- }
- if ($property) {
- return isset($info[$type]) && isset($info[$type][$property]) ? $info[$type][$property] : NULL;
- }
- elseif ($type) {
- return isset($info[$type]) ? $info[$type] : array();
- }
- else {
- return $info;
- }
- }
|