diff --git a/components/date.inc b/components/date.inc index b79f31f..b9db4e4 100644 --- a/components/date.inc +++ b/components/date.inc @@ -134,6 +134,7 @@ function _webform_render_date($component, $value = NULL, $filter = TRUE) { '#theme_wrappers' => array('webform_element'), '#element_validate' => array('webform_validate_date'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); if ($component['extra']['datepicker']) { diff --git a/components/email.inc b/components/email.inc index f8fb148..38229e8 100644 --- a/components/email.inc +++ b/components/email.inc @@ -120,6 +120,7 @@ function _webform_render_email($component, $value = NULL, $filter = TRUE) { '#element_validate' => array('_webform_validate_email'), '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); // Add an e-mail class for identifying the difference from normal textfields. diff --git a/components/fieldset.inc b/components/fieldset.inc index 92875b4..e4e9fdc 100644 --- a/components/fieldset.inc +++ b/components/fieldset.inc @@ -62,6 +62,7 @@ function _webform_render_fieldset($component, $value = NULL, $filter = TRUE) { '#attributes' => array('class' => array('webform-component-fieldset'), 'id' => 'webform-component-' . $component['form_key']), '#pre_render' => array('webform_fieldset_prerender'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); // Hide the fieldset title if #title_display is 'none'. diff --git a/components/file.inc b/components/file.inc index 6df31cb..f6dadb3 100644 --- a/components/file.inc +++ b/components/file.inc @@ -367,6 +367,7 @@ function _webform_render_file($component, $value = NULL, $filter = TRUE) { '#weight' => $component['weight'], '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); return $element; diff --git a/components/grid.inc b/components/grid.inc index a53192b..6700478 100644 --- a/components/grid.inc +++ b/components/grid.inc @@ -157,6 +157,7 @@ function _webform_render_grid($component, $value = NULL, $filter = TRUE) { '#theme_wrappers' => array('webform_element'), '#process' => array('webform_expand_grid'), '#webform_component' => $component, + '#translatable' => array('title', 'description', 'grid_options', 'grid_questions'), ); if ($value) { @@ -180,6 +181,8 @@ function webform_expand_grid($element) { if (!empty($element['#qrand'])) { _webform_shuffle_options($questions); } + + $element['#options'] = $options; foreach ($questions as $key => $question) { if ($question != '') { @@ -193,6 +196,9 @@ function webform_expand_grid($element) { // Webform handles validation manually. '#validated' => TRUE, '#webform_validated' => FALSE, + + '#translatable' => array('title'), + ); } } @@ -231,7 +237,7 @@ function _webform_display_grid($component, $value, $format = 'html') { foreach ($questions as $key => $question) { if ($question !== '') { - $element[$question] = array( + $element[$key] = array( '#title' => $question, '#value' => isset($value[$key]) ? $value[$key] : NULL, ); @@ -399,17 +405,13 @@ function theme_webform_grid($variables) { $rows = array(); $header = array(array('data' => '', 'class' => array('webform-grid-question'))); - $first = TRUE; + // Set the header for the table. + foreach ($element['#options'] as $option) { + $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option')); + } foreach (element_children($element) as $key) { $question_element = $element[$key]; - // Set the header for the table. - if ($first) { - foreach ($question_element['#options'] as $option) { - $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option')); - } - $first = FALSE; - } // Create a row with the question title. $row = array(array('data' => _webform_filter_xss($question_element['#title']), 'class' => array('webform-grid-question'))); diff --git a/components/hidden.inc b/components/hidden.inc index 5930da6..4ac823e 100644 --- a/components/hidden.inc +++ b/components/hidden.inc @@ -62,6 +62,7 @@ function _webform_render_hidden($component, $value = NULL, $filter = TRUE) { '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'], '#default_value' => $filter ? _webform_filter_values($component['value']) : $component['value'], '#weight' => $component['weight'], + '#translatable' => array('title'), ); if (isset($value[0])) { diff --git a/components/markup.inc b/components/markup.inc index 064abf5..eb5b470 100644 --- a/components/markup.inc +++ b/components/markup.inc @@ -65,6 +65,7 @@ function _webform_render_markup($component, $value = NULL, $filter = TRUE) { '#format' => $component['extra']['format'], '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'markup'), ); // TODO: Remove when #markup becomes available in D7. diff --git a/components/number.inc b/components/number.inc index 13ddbcc..3b67278 100644 --- a/components/number.inc +++ b/components/number.inc @@ -256,6 +256,7 @@ function _webform_render_number($component, $value = NULL, $filter = TRUE) { '#step' => abs($component['extra']['step']), '#integer' => $component['extra']['integer'], '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); // Flip the min and max properties to make min less than max if needed. diff --git a/components/pagebreak.inc b/components/pagebreak.inc index 95c838c..d832c7b 100644 --- a/components/pagebreak.inc +++ b/components/pagebreak.inc @@ -71,6 +71,7 @@ function _webform_display_pagebreak($component, $value = NULL, $format = 'html') '#weight' => $component['weight'], '#format' => $format, '#webform_component' => $component, + '#translatable' => array('title'), ); return $element; } diff --git a/components/select.inc b/components/select.inc index f3ffafe..837b7b0 100644 --- a/components/select.inc +++ b/components/select.inc @@ -271,6 +271,7 @@ function _webform_render_select($component, $value = NULL, $filter = TRUE) { '#theme_wrappers' => array('webform_element'), '#pre_render' => array(), // Needed to disable double-wrapping of radios and checkboxes. '#webform_component' => $component, + '#translatable' => array('title', 'description', 'options'), ); // Convert the user-entered options list into an array. @@ -451,6 +452,7 @@ function _webform_display_select($component, $value, $format = 'html') { return array( '#title' => $component['name'], '#weight' => $component['weight'], + '#options' => _webform_select_options($component), '#theme' => 'webform_display_select', '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'), '#format' => $format, @@ -505,7 +507,8 @@ function theme_webform_display_select($variables) { $component = $element['#webform_component']; // Convert submitted 'safe' values to un-edited, original form. - $options = _webform_select_options($component, TRUE); + $options = $element['#options']; + $items = array(); if ($component['extra']['multiple']) { diff --git a/components/textarea.inc b/components/textarea.inc index 7523cd9..019e5a4 100644 --- a/components/textarea.inc +++ b/components/textarea.inc @@ -112,6 +112,7 @@ function _webform_render_textarea($component, $value = NULL, $filter = TRUE) { '#resizable' => (bool) $component['extra']['resizable'], // MUST be FALSE to disable. '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); if ($component['extra']['disabled']) { diff --git a/components/textfield.inc b/components/textfield.inc index 7c45881..8a2bff1 100644 --- a/components/textfield.inc +++ b/components/textfield.inc @@ -135,6 +135,7 @@ function _webform_render_textfield($component, $value = NULL, $filter = TRUE) { '#attributes' => $component['extra']['attributes'], '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); if ($component['extra']['disabled']) { diff --git a/components/time.inc b/components/time.inc index f417020..e69cba0 100644 --- a/components/time.inc +++ b/components/time.inc @@ -99,6 +99,7 @@ function _webform_render_time($component, $value = NULL, $filter = TRUE) { '#theme' => 'webform_time', '#theme_wrappers' => array('webform_element'), '#webform_component' => $component, + '#translatable' => array('title', 'description'), ); // Set the value from Webform if available. diff --git a/includes/webform.components.inc b/includes/webform.components.inc index 18e74bd..11f61a3 100644 --- a/includes/webform.components.inc +++ b/includes/webform.components.inc @@ -756,6 +756,9 @@ function webform_component_insert(&$component) { )) ->execute(); + // Create translation source for all the translatable poperties. + webform_component_update_translation_strings($component); + // Post-insert actions. module_invoke_all('webform_component_insert', $component); @@ -775,6 +778,8 @@ function webform_component_update($component) { $function = $module . '_webform_component_presave'; $function($component); } + // Update / create translation source for all the translatable poperties. + webform_component_update_translation_strings($component); $component['value'] = isset($component['value']) ? $component['value'] : NULL; $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0; @@ -834,6 +839,9 @@ function webform_component_delete($node, $component) { $child_component = $node->webform['components'][$row->cid]; webform_component_delete($node, $child_component); } + + // Remove translation source for all the translatable poperties. + webform_component_delete_translation_strings($component); // Post-delete actions. module_invoke_all('webform_component_delete', $component); @@ -1071,3 +1079,97 @@ function webform_validate_unique($element, $form_state) { } } } + + +/** + * Update / create translation source for all the translatable poperties. + * + * @param array $component + */ +function webform_component_update_translation_strings(&$component) { + if (module_exists('i18n_string')) { + // Fill in the the default values for the missing properties. + webform_component_defaults($component); + // Render the 'render' FAPI array for the component. + $element = webform_component_invoke($component['type'], 'render', $component, NULL, 'html'); + // Parse the renderable array to find the translatable properties and + // update / create translation source for them + $component['extra']['translated_strings'] = _webform_component_translation_parse($element, $component); + // Render the 'display' FAPI array for the component. + $element = webform_component_invoke($component['type'], 'display', $component, NULL, 'html'); + // Parse the renderable array to find the translatable properties and + // update / create translation source for them + $component['extra']['translated_strings'] = array_merge($component['extra']['translated_strings'], _webform_component_translation_parse($element, $component)); + } +} + +/** + * Remove translation source for all the translatable poperties. + * + * @param array $component + */ +function webform_component_delete_translation_strings($component) { + if (module_exists('i18n_string')) { + if (isset($component['extra']['translated_strings'])) { + foreach ($component['extra']['translated_strings'] as $name) { + i18n_string_remove($name); + } + } + } +} + +/** + * Parse a component renderable array to find the translatable properties and + * create / update or remove translation source for them. + * + * @param array $element + * The renderable array to be parsed. + * @param array $component + * The component which was rendered. + */ +function _webform_component_translation_parse($element, $component) { + $translated_properies = array(); + + if (!isset($element['#parents'])) { + $element['#parents'] = array(); + } + + if (isset($element['#translatable']) && is_array($element['#translatable'])) { + foreach ($element['#translatable'] as $key) { + if (isset($element['#' . $key]) && $element['#' . $key] != '') { + if (isset($element['#parents']) && count($element['#parents'])) { + $property = '[' . implode('][', $element['#parents']) . ']#' . $key; + } + else { + $property = '#' . $key; + } + if (is_array($element['#' . $key])) { + // If the translatable property is an array, we translate the children. + foreach ($element['#' . $key] as $elem_key => $elem_value) { + $name = implode(':', array('webform', $component['nid'], $component['cid'], $property . '-' . $elem_key)); + $translated_properies[] = $name; + webform_tt($name, $elem_value, NULL, TRUE); + } + } + else { + // If the translatable property is not an array, it can be treated as + // a string. + $name = implode(':', array('webform', $component['nid'], $component['cid'], $property)); + $translated_properies[] = $name; + webform_tt($name, $element['#' . $key], NULL, TRUE); + } + } + } + } + + // Recursevly call the function on the children, after adding the children + // name to its #parents array. + foreach (element_children($element) as $child) { + $element[$child]['#parents'] = $element['#parents']; + $element[$child]['#parents'][] = $child; + // Add the translated propertied to the list. + $translated_properies = array_merge($translated_properies, _webform_component_translation_parse($element[$child], $component)); + } + + return $translated_properies; +} diff --git a/webform.module b/webform.module index ea75f0a..c1d34f4 100644 --- a/webform.module +++ b/webform.module @@ -720,6 +720,47 @@ function webform_element_info() { return $elements; } + + /** + * Implements hook_i18n_string_info(). + */ +function webform_i18n_string_info() { + $groups['webform'] = array( + 'title' => t('Webform'), + 'description' => t('Localizable properties of webforms, like title, select options, and others.'), + 'format' => FALSE, // This group doesn't have strings with format + 'list' => FALSE, // This group cannot list all strings + //'refresh callback' => 'webform_i18n_string_refresh', + ); + return $groups; +} + +/** + * Update / create translation source for all the translatable poperties of + * all the components + */ +function webform_i18n_string_refresh() { + $components = db_select('webform_component', 'wc') + ->fields('wc') + ->execute() + ->fetchAllAssoc('cid'); + + foreach ($components as $component) { + $component = (array) $component; + $component['extra'] = unserialize($component['extra']); + + module_load_include('inc', 'webform', 'includes/webform.components'); + webform_component_update_translation_strings($component); + + $component['extra'] = serialize($component['extra']); + drupal_write_record('webform_component', $component, array('nid', 'cid')); + } + + return TRUE; +} + + + /** * Implements hook_webform_component_info(). */ @@ -2094,6 +2135,9 @@ function _webform_client_form_add_component($node, $component, $component_value, // This component is display only. $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value']; if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) { + + // Translate the translatable properties. + _webform_translate_component($display_element, $component); // The form_builder() function usually adds #parents and #id for us, but // because these are not marked for #input, we need to add them manually. if (!isset($display_element['#parents'])) { @@ -2111,6 +2155,8 @@ function _webform_client_form_add_component($node, $component, $component_value, // Add this user-defined field to the form (with all the values that are always available). $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL; if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) { + // Translate the translatable properties. + _webform_translate_component($element, $component); $parent_fieldset[$component['form_key']] = $element; // Override the value if one already exists in the form state. @@ -2147,6 +2193,62 @@ function _webform_client_form_add_component($node, $component, $component_value, } } +/** + * Translates the component properties that are translatable. These are found + * in under 'translated_strigs' in the 'extra' array of the component, which + * is build when the component is inserted / updated, or when all webform + * strings are updated from admin/config/regional/translate/i18n_string. + * @param array $element + * The FAPI renderable array of the component instance. + * @param array $component + * The component. + */ +function _webform_translate_component(&$element, $component) { + if (isset($component['extra']['translated_strings']) && is_array($component['extra']['translated_strings'])) { + foreach ($component['extra']['translated_strings'] as $name) { + $name_list = explode(':', $name); + $current_element = &$element; + if (strpos($name_list[3], '[') !== FALSE) { + // The property is deeper in the renderable array, we must extract the + // the place where it is. + list ($children, $property) = explode(']#', $name_list[3]); + // Remove the '[' from the begining of the string. + $children = drupal_substr($children, 1); + $children_array = explode('][', $children); + foreach ($children_array as $child) { + if (isset($current_element[$child])) { + $current_element = &$current_element[$child]; + } + else { + continue; + } + } + } + else { + // Remove the '#' from the begining of the property, for consistency. + $property = drupal_substr($name_list[3], 1); + } + + if (strpos($property, '-') !== FALSE) { + // If property is array, we extract the key from the property. + list ($property, $key) = explode('-', $property); + if (isset($current_element['#' . $property][$key])) { + $current_element['#' . $property][$key] = webform_tt($name, $current_element['#' . $property][$key]); + } + } + else { + // Else we can treat the property as string. + if (isset($current_element['#' . $property])) { + $current_element['#' . $property] = webform_tt($name, $current_element['#' . $property]); + } + } + } + } +} + + + + function webform_client_form_validate($form, &$form_state) { $node = node_load($form_state['values']['details']['nid']); $finished = $form_state['values']['details']['finished']; @@ -3652,11 +3754,16 @@ function webform_strtotime($date) { } /** - * Wrapper function for tt() if i18nstrings enabled. + * Wrapper function for i18n_string() if i18nstrings enabled. */ function webform_tt($name, $string, $langcode = NULL, $update = FALSE) { - if (function_exists('tt')) { - return tt($name, $string, $langcode, $update); + +if (function_exists('i18n_string')) { + $options = array( + 'langcode' => $langcode, + 'update' => $update, + ); + return i18n_string($name, $string, $options); } else { return $string;